diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 1667d8093f..fe9f3f7a50 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -224,6 +224,7 @@ struct NumericData * * The value represented by a NumericVar is determined by the sign, weight, * ndigits, and digits[] array. + * * Note: the first digit of a NumericVar's value is assumed to be multiplied * by NBASE ** weight. Another way to say it is that there are weight+1 * digits before the decimal point. It is possible to have weight < 0. @@ -255,6 +256,11 @@ struct NumericData * Note that rscale is not stored in variables --- it's figured on-the-fly * from the dscales of the inputs. * + * While we consistently use "weight" to refer to the base-NBASE weight of + * a numeric value, it is convenient in some scale-related calculations to + * make use of the base-10 weight (ie, the approximate log10 of the value). + * To avoid confusion, such a decimal-units weight is called a "dweight". + * * NB: All the variable-level functions are written in a style that makes it * possible to give one and the same variable as argument and destination. * This is feasible because the digit buffer is separate from the variable. @@ -360,20 +366,6 @@ static NumericDigit const_zero_point_nine_data[1] = {9}; static NumericVar const_zero_point_nine = {1, -1, NUMERIC_POS, 1, NULL, const_zero_point_nine_data}; -#if DEC_DIGITS == 4 -static NumericDigit const_zero_point_01_data[1] = {100}; -static NumericVar const_zero_point_01 = -{1, -1, NUMERIC_POS, 2, NULL, const_zero_point_01_data}; -#elif DEC_DIGITS == 2 -static NumericDigit const_zero_point_01_data[1] = {1}; -static NumericVar const_zero_point_01 = -{1, -1, NUMERIC_POS, 2, NULL, const_zero_point_01_data}; -#elif DEC_DIGITS == 1 -static NumericDigit const_zero_point_01_data[1] = {1}; -static NumericVar const_zero_point_01 = -{1, -2, NUMERIC_POS, 2, NULL, const_zero_point_01_data}; -#endif - #if DEC_DIGITS == 4 static NumericDigit const_one_point_one_data[2] = {1, 1000}; #elif DEC_DIGITS == 2 @@ -477,7 +469,7 @@ static void floor_var(NumericVar *var, NumericVar *result); static void sqrt_var(NumericVar *arg, NumericVar *result, int rscale); static void exp_var(NumericVar *arg, NumericVar *result, int rscale); -static void exp_var_internal(NumericVar *arg, NumericVar *result, int rscale); +static int estimate_ln_dweight(NumericVar *var); static void ln_var(NumericVar *arg, NumericVar *result, int rscale); static void log_var(NumericVar *base, NumericVar *num, NumericVar *result); static void power_var(NumericVar *base, NumericVar *exp, NumericVar *result); @@ -2697,7 +2689,7 @@ numeric_ln(PG_FUNCTION_ARGS) Numeric res; NumericVar arg; NumericVar result; - int dec_digits; + int ln_dweight; int rscale; /* @@ -2709,16 +2701,10 @@ numeric_ln(PG_FUNCTION_ARGS) init_var_from_num(num, &arg); init_var(&result); - /* Approx decimal digits before decimal point */ - dec_digits = (arg.weight + 1) * DEC_DIGITS; - - if (dec_digits > 1) - rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(dec_digits - 1); - else if (dec_digits < 1) - rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(1 - dec_digits); - else - rscale = NUMERIC_MIN_SIG_DIGITS; + /* Estimated dweight of logarithm */ + ln_dweight = estimate_ln_dweight(&arg); + rscale = NUMERIC_MIN_SIG_DIGITS - ln_dweight; rscale = Max(rscale, arg.dscale); rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); @@ -5720,17 +5706,35 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, int carry; int maxdig; int newdig; + int var1ndigits; + int var2ndigits; + NumericDigit *var1digits; + NumericDigit *var2digits; NumericDigit *res_digits; int i, - ri, i1, i2; + /* + * Arrange for var1 to be the shorter of the two numbers. This improves + * performance because the inner multiplication loop is much simpler than + * the outer loop, so it's better to have a smaller number of iterations + * of the outer loop. This also reduces the number of times that the + * accumulator array needs to be normalized. + */ + if (var1->ndigits > var2->ndigits) + { + NumericVar *tmp = var1; + + var1 = var2; + var2 = tmp; + } + /* copy these values into local vars for speed in inner loop */ - int var1ndigits = var1->ndigits; - int var2ndigits = var2->ndigits; - NumericDigit *var1digits = var1->digits; - NumericDigit *var2digits = var2->digits; + var1ndigits = var1->ndigits; + var2ndigits = var2->ndigits; + var1digits = var1->digits; + var2digits = var2->digits; if (var1ndigits == 0 || var2ndigits == 0) { @@ -5748,39 +5752,28 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, res_weight = var1->weight + var2->weight + 2; /* - * Determine number of result digits to compute. If the exact result + * Determine the number of result digits to compute. If the exact result * would have more than rscale fractional digits, truncate the computation - * with MUL_GUARD_DIGITS guard digits. We do that by pretending that one - * or both inputs have fewer digits than they really do. + * with MUL_GUARD_DIGITS guard digits, i.e., ignore input digits that + * would only contribute to the right of that. (This will give the exact + * rounded-to-rscale answer unless carries out of the ignored positions + * would have propagated through more than MUL_GUARD_DIGITS digits.) + * + * Note: an exact computation could not produce more than var1ndigits + + * var2ndigits digits, but we allocate one extra output digit in case + * rscale-driven rounding produces a carry out of the highest exact digit. */ res_ndigits = var1ndigits + var2ndigits + 1; - maxdigits = res_weight + 1 + (rscale * DEC_DIGITS) + MUL_GUARD_DIGITS; - if (res_ndigits > maxdigits) + maxdigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS + + MUL_GUARD_DIGITS; + res_ndigits = Min(res_ndigits, maxdigits); + + if (res_ndigits < 3) { - if (maxdigits < 3) - { - /* no useful precision at all in the result... */ - zero_var(result); - result->dscale = rscale; - return; - } - /* force maxdigits odd so that input ndigits can be equal */ - if ((maxdigits & 1) == 0) - maxdigits++; - if (var1ndigits > var2ndigits) - { - var1ndigits -= res_ndigits - maxdigits; - if (var1ndigits < var2ndigits) - var1ndigits = var2ndigits = (var1ndigits + var2ndigits) / 2; - } - else - { - var2ndigits -= res_ndigits - maxdigits; - if (var2ndigits < var1ndigits) - var1ndigits = var2ndigits = (var1ndigits + var2ndigits) / 2; - } - res_ndigits = maxdigits; - Assert(res_ndigits == var1ndigits + var2ndigits + 1); + /* All input digits will be ignored; so result is zero */ + zero_var(result); + result->dscale = rscale; + return; } /* @@ -5802,8 +5795,16 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, dig = (int *) palloc0(res_ndigits * sizeof(int)); maxdig = 0; - ri = res_ndigits - 1; - for (i1 = var1ndigits - 1; i1 >= 0; ri--, i1--) + /* + * The least significant digits of var1 should be ignored if they don't + * contribute directly to the first res_ndigits digits of the result that + * we are computing. + * + * Digit i1 of var1 and digit i2 of var2 are multiplied and added to digit + * i1+i2+2 of the accumulator array, so we need only consider digits of + * var1 for which i1 <= res_ndigits - 3. + */ + for (i1 = Min(var1ndigits - 1, res_ndigits - 3); i1 >= 0; i1--) { int var1digit = var1digits[i1]; @@ -5833,9 +5834,14 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, maxdig = 1 + var1digit; } - /* Add appropriate multiple of var2 into the accumulator */ - i = ri; - for (i2 = var2ndigits - 1; i2 >= 0; i2--) + /* + * Add the appropriate multiple of var2 into the accumulator. + * + * As above, digits of var2 can be ignored if they don't contribute, + * so we only include digits for which i1+i2+2 <= res_ndigits - 1. + */ + for (i2 = Min(var2ndigits - 1, res_ndigits - i1 - 3), i = i1 + i2 + 2; + i2 >= 0; i2--) dig[i--] += var1digit * var2digits[i2]; } @@ -6635,120 +6641,81 @@ sqrt_var(NumericVar *arg, NumericVar *result, int rscale) /* * exp_var() - * - * Raise e to the power of x + * Raise e to the power of x, computed to rscale fractional digits */ static void exp_var(NumericVar *arg, NumericVar *result, int rscale) { NumericVar x; - int xintval; - bool xneg = FALSE; - int local_rscale; - - /*---------- - * We separate the integral and fraction parts of x, then compute - * e^x = e^xint * e^xfrac - * where e = exp(1) and e^xfrac = exp(xfrac) are computed by - * exp_var_internal; the limited range of inputs allows that routine - * to do a good job with a simple Taylor series. Raising e^xint is - * done by repeated multiplications in power_var_int. - *---------- - */ - init_var(&x); - - set_var_from_var(arg, &x); - - if (x.sign == NUMERIC_NEG) - { - xneg = TRUE; - x.sign = NUMERIC_POS; - } - - /* Extract the integer part, remove it from x */ - xintval = 0; - while (x.weight >= 0) - { - xintval *= NBASE; - if (x.ndigits > 0) - { - xintval += x.digits[0]; - x.digits++; - x.ndigits--; - } - x.weight--; - /* Guard against overflow */ - if (xintval >= NUMERIC_MAX_RESULT_SCALE * 3) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("argument for function \"exp\" too big"))); - } - - /* Select an appropriate scale for internal calculation */ - local_rscale = rscale + MUL_GUARD_DIGITS * 2; - - /* Compute e^xfrac */ - exp_var_internal(&x, result, local_rscale); - - /* If there's an integer part, multiply by e^xint */ - if (xintval > 0) - { - NumericVar e; - - init_var(&e); - exp_var_internal(&const_one, &e, local_rscale); - power_var_int(&e, xintval, &e, local_rscale); - mul_var(&e, result, result, local_rscale); - free_var(&e); - } - - /* Compensate for input sign, and round to requested rscale */ - if (xneg) - div_var_fast(&const_one, result, result, rscale, true); - else - round_var(result, rscale); - - free_var(&x); -} - - -/* - * exp_var_internal() - - * - * Raise e to the power of x, where 0 <= x <= 1 - * - * NB: the result should be good to at least rscale digits, but it has - * *not* been rounded off; the caller must do that if wanted. - */ -static void -exp_var_internal(NumericVar *arg, NumericVar *result, int rscale) -{ - NumericVar x; - NumericVar xpow; - NumericVar ifac; NumericVar elem; NumericVar ni; - int ndiv2 = 0; + double val; + int dweight; + int ndiv2; + int sig_digits; int local_rscale; init_var(&x); - init_var(&xpow); - init_var(&ifac); init_var(&elem); init_var(&ni); set_var_from_var(arg, &x); - Assert(x.sign == NUMERIC_POS); + /* + * Estimate the dweight of the result using floating point arithmetic, so + * that we can choose an appropriate local rscale for the calculation. + */ + val = numericvar_to_double_no_overflow(&x); - local_rscale = rscale + 8; + /* Guard against overflow */ + if (Abs(val) >= NUMERIC_MAX_RESULT_SCALE * 3) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); - /* Reduce input into range 0 <= x <= 0.01 */ - while (cmp_var(&x, &const_zero_point_01) > 0) + /* decimal weight = log10(e^x) = x * log10(e) */ + dweight = (int) (val * 0.434294481903252); + + /* + * Reduce x to the range -0.01 <= x <= 0.01 (approximately) by dividing by + * 2^n, to improve the convergence rate of the Taylor series. + */ + if (Abs(val) > 0.01) { - ndiv2++; - local_rscale++; - mul_var(&x, &const_zero_point_five, &x, x.dscale + 1); + NumericVar tmp; + + init_var(&tmp); + set_var_from_var(&const_two, &tmp); + + ndiv2 = 1; + val /= 2; + + while (Abs(val) > 0.01) + { + ndiv2++; + val /= 2; + add_var(&tmp, &tmp, &tmp); + } + + local_rscale = x.dscale + ndiv2; + div_var_fast(&x, &tmp, &x, local_rscale, true); + + free_var(&tmp); } + else + ndiv2 = 0; + + /* + * Set the scale for the Taylor series expansion. The final result has + * (dweight + rscale + 1) significant digits. In addition, we have to + * raise the Taylor series result to the power 2^ndiv2, which introduces + * an error of up to around log10(2^ndiv2) digits, so work with this many + * extra digits of precision (plus a few more for good measure). + */ + sig_digits = 1 + dweight + rscale + (int) (ndiv2 * 0.301029995663981); + sig_digits = Max(sig_digits, 0) + 8; + + local_rscale = sig_digits - 1; /* * Use the Taylor series @@ -6759,35 +6726,121 @@ exp_var_internal(NumericVar *arg, NumericVar *result, int rscale) * We run the series until the terms fall below the local_rscale limit. */ add_var(&const_one, &x, result); - set_var_from_var(&x, &xpow); - set_var_from_var(&const_one, &ifac); - set_var_from_var(&const_one, &ni); - for (;;) + mul_var(&x, &x, &elem, local_rscale); + set_var_from_var(&const_two, &ni); + div_var_fast(&elem, &ni, &elem, local_rscale, true); + + while (elem.ndigits != 0) { - add_var(&ni, &const_one, &ni); - mul_var(&xpow, &x, &xpow, local_rscale); - mul_var(&ifac, &ni, &ifac, 0); - div_var_fast(&xpow, &ifac, &elem, local_rscale, true); - - if (elem.ndigits == 0) - break; - add_var(result, &elem, result); + + mul_var(&elem, &x, &elem, local_rscale); + add_var(&ni, &const_one, &ni); + div_var_fast(&elem, &ni, &elem, local_rscale, true); } - /* Compensate for argument range reduction */ + /* + * Compensate for the argument range reduction. Since the weight of the + * result doubles with each multiplication, we can reduce the local rscale + * as we proceed. + */ while (ndiv2-- > 0) + { + local_rscale = sig_digits - result->weight * 2 * DEC_DIGITS; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); mul_var(result, result, result, local_rscale); + } + + /* Round to requested rscale */ + round_var(result, rscale); free_var(&x); - free_var(&xpow); - free_var(&ifac); free_var(&elem); free_var(&ni); } +/* + * Estimate the dweight of the most significant decimal digit of the natural + * logarithm of a number. + * + * Essentially, we're approximating log10(abs(ln(var))). This is used to + * determine the appropriate rscale when computing natural logarithms. + */ +static int +estimate_ln_dweight(NumericVar *var) +{ + int ln_dweight; + + if (cmp_var(var, &const_zero_point_nine) >= 0 && + cmp_var(var, &const_one_point_one) <= 0) + { + /* + * 0.9 <= var <= 1.1 + * + * ln(var) has a negative weight (possibly very large). To get a + * reasonably accurate result, estimate it using ln(1+x) ~= x. + */ + NumericVar x; + + init_var(&x); + sub_var(var, &const_one, &x); + + if (x.ndigits > 0) + { + /* Use weight of most significant decimal digit of x */ + ln_dweight = x.weight * DEC_DIGITS + (int) log10(x.digits[0]); + } + else + { + /* x = 0. Since ln(1) = 0 exactly, we don't need extra digits */ + ln_dweight = 0; + } + + free_var(&x); + } + else + { + /* + * Estimate the logarithm using the first couple of digits from the + * input number. This will give an accurate result whenever the input + * is not too close to 1. + */ + if (var->ndigits > 0) + { + int digits; + int dweight; + double ln_var; + + digits = var->digits[0]; + dweight = var->weight * DEC_DIGITS; + + if (var->ndigits > 1) + { + digits = digits * NBASE + var->digits[1]; + dweight -= DEC_DIGITS; + } + + /*---------- + * We have var ~= digits * 10^dweight + * so ln(var) ~= ln(digits) + dweight * ln(10) + *---------- + */ + ln_var = log((double) digits) + dweight * 2.302585092994046; + ln_dweight = (int) log10(Abs(ln_var)); + } + else + { + /* Caller should fail on ln(0), but for the moment return zero */ + ln_dweight = 0; + } + } + + return ln_dweight; +} + + /* * ln_var() - * @@ -6814,8 +6867,6 @@ ln_var(NumericVar *arg, NumericVar *result, int rscale) (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), errmsg("cannot take logarithm of a negative number"))); - local_rscale = rscale + 8; - init_var(&x); init_var(&xx); init_var(&ni); @@ -6825,16 +6876,25 @@ ln_var(NumericVar *arg, NumericVar *result, int rscale) set_var_from_var(arg, &x); set_var_from_var(&const_two, &fact); - /* Reduce input into range 0.9 < x < 1.1 */ + /* + * Reduce input into range 0.9 < x < 1.1 with repeated sqrt() operations. + * + * The final logarithm will have up to around rscale+6 significant digits. + * Each sqrt() will roughly halve the weight of x, so adjust the local + * rscale as we work so that we keep this many significant digits at each + * step (plus a few more for good measure). + */ while (cmp_var(&x, &const_zero_point_nine) <= 0) { - local_rscale++; + local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); sqrt_var(&x, &x, local_rscale); mul_var(&fact, &const_two, &fact, 0); } while (cmp_var(&x, &const_one_point_one) >= 0) { - local_rscale++; + local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); sqrt_var(&x, &x, local_rscale); mul_var(&fact, &const_two, &fact, 0); } @@ -6850,6 +6910,8 @@ ln_var(NumericVar *arg, NumericVar *result, int rscale) * The convergence of this is not as fast as one would like, but is * tolerable given that z is small. */ + local_rscale = rscale + 8; + sub_var(&x, &const_one, result); add_var(&x, &const_one, &elem); div_var_fast(result, &elem, result, local_rscale, true); @@ -6896,42 +6958,47 @@ log_var(NumericVar *base, NumericVar *num, NumericVar *result) { NumericVar ln_base; NumericVar ln_num; - int dec_digits; + int ln_base_dweight; + int ln_num_dweight; + int result_dweight; int rscale; - int local_rscale; + int ln_base_rscale; + int ln_num_rscale; init_var(&ln_base); init_var(&ln_num); - /* Set scale for ln() calculations --- compare numeric_ln() */ - - /* Approx decimal digits before decimal point */ - dec_digits = (num->weight + 1) * DEC_DIGITS; - - if (dec_digits > 1) - rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(dec_digits - 1); - else if (dec_digits < 1) - rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(1 - dec_digits); - else - rscale = NUMERIC_MIN_SIG_DIGITS; + /* Estimated dweights of ln(base), ln(num) and the final result */ + ln_base_dweight = estimate_ln_dweight(base); + ln_num_dweight = estimate_ln_dweight(num); + result_dweight = ln_num_dweight - ln_base_dweight; + /* + * Select the scale of the result so that it will have at least + * NUMERIC_MIN_SIG_DIGITS significant digits and is not less than either + * input's display scale. + */ + rscale = NUMERIC_MIN_SIG_DIGITS - result_dweight; rscale = Max(rscale, base->dscale); rscale = Max(rscale, num->dscale); rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); - local_rscale = rscale + 8; + /* + * Set the scales for ln(base) and ln(num) so that they each have more + * significant digits than the final result. + */ + ln_base_rscale = rscale + result_dweight - ln_base_dweight + 8; + ln_base_rscale = Max(ln_base_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + ln_num_rscale = rscale + result_dweight - ln_num_dweight + 8; + ln_num_rscale = Max(ln_num_rscale, NUMERIC_MIN_DISPLAY_SCALE); /* Form natural logarithms */ - ln_var(base, &ln_base, local_rscale); - ln_var(num, &ln_num, local_rscale); - - ln_base.dscale = rscale; - ln_num.dscale = rscale; - - /* Select scale for division result */ - rscale = select_div_scale(&ln_num, &ln_base); + ln_var(base, &ln_base, ln_base_rscale); + ln_var(num, &ln_num, ln_num_rscale); + /* Divide and round to the required scale */ div_var_fast(&ln_num, &ln_base, result, rscale, true); free_var(&ln_num); @@ -6951,7 +7018,7 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result) { NumericVar ln_base; NumericVar ln_num; - int dec_digits; + int ln_dweight; int rscale; int local_rscale; double val; @@ -6982,7 +7049,7 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result) } /* - * This avoids log(0) for cases of 0 raised to a non-integer. 0 ^ 0 + * This avoids log(0) for cases of 0 raised to a non-integer. 0 ^ 0 is * handled by power_var_int(). */ if (cmp_var(base, &const_zero) == 0) @@ -6995,49 +7062,50 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result) init_var(&ln_base); init_var(&ln_num); - /* Set scale for ln() calculation --- need extra accuracy here */ + /*---------- + * Decide on the scale for the ln() calculation. For this we need an + * estimate of the weight of the result, which we obtain by doing an + * initial low-precision calculation of exp * ln(base). + * + * We want result = e ^ (exp * ln(base)) + * so result dweight = log10(result) = exp * ln(base) * log10(e) + *---------- + */ + ln_dweight = estimate_ln_dweight(base); - /* Approx decimal digits before decimal point */ - dec_digits = (base->weight + 1) * DEC_DIGITS; - - if (dec_digits > 1) - rscale = NUMERIC_MIN_SIG_DIGITS * 2 - (int) log10(dec_digits - 1); - else if (dec_digits < 1) - rscale = NUMERIC_MIN_SIG_DIGITS * 2 - (int) log10(1 - dec_digits); - else - rscale = NUMERIC_MIN_SIG_DIGITS * 2; - - rscale = Max(rscale, base->dscale * 2); - rscale = Max(rscale, exp->dscale * 2); - rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE * 2); - rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE * 2); - - local_rscale = rscale + 8; + local_rscale = 8 - ln_dweight; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + local_rscale = Min(local_rscale, NUMERIC_MAX_DISPLAY_SCALE); ln_var(base, &ln_base, local_rscale); mul_var(&ln_base, exp, &ln_num, local_rscale); - /* Set scale for exp() -- compare numeric_exp() */ - - /* convert input to float8, ignoring overflow */ val = numericvar_to_double_no_overflow(&ln_num); - /* - * log10(result) = num * log10(e), so this is approximately the weight: - */ - val *= 0.434294481903252; + val *= 0.434294481903252; /* approximate decimal result weight */ /* limit to something that won't cause integer overflow */ val = Max(val, -NUMERIC_MAX_RESULT_SCALE); val = Min(val, NUMERIC_MAX_RESULT_SCALE); + /* choose the result scale */ rscale = NUMERIC_MIN_SIG_DIGITS - (int) val; rscale = Max(rscale, base->dscale); rscale = Max(rscale, exp->dscale); rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + /* set the scale for the real exp * ln(base) calculation */ + local_rscale = rscale + (int) val - ln_dweight + 8; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + /* and do the real calculation */ + + ln_var(base, &ln_base, local_rscale); + + mul_var(&ln_base, exp, &ln_num, local_rscale); + exp_var(&ln_num, result, rscale); free_var(&ln_num); @@ -7052,6 +7120,10 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result) static void power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale) { + double f; + int p; + int i; + int sig_digits; unsigned int mask; bool neg; NumericVar base_prod; @@ -7086,15 +7158,75 @@ power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale) break; } + /* Handle the special case where the base is zero */ + if (base->ndigits == 0) + { + if (exp < 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + zero_var(result); + result->dscale = rscale; + return; + } + /* * The general case repeatedly multiplies base according to the bit - * pattern of exp. We do the multiplications with some extra precision. + * pattern of exp. + * + * First we need to estimate the weight of the result so that we know how + * many significant digits are needed. + */ + f = base->digits[0]; + p = base->weight * DEC_DIGITS; + + for (i = 1; i < base->ndigits && i * DEC_DIGITS < 16; i++) + { + f = f * NBASE + base->digits[i]; + p -= DEC_DIGITS; + } + + /*---------- + * We have base ~= f * 10^p + * so log10(result) = log10(base^exp) ~= exp * (log10(f) + p) + *---------- + */ + f = exp * (log10(f) + p); + + /* + * Apply crude overflow/underflow tests so we can exit early if the result + * certainly will overflow/underflow. + */ + if (f > 3 * SHRT_MAX * DEC_DIGITS) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + if (f + 1 < -rscale || f + 1 < -NUMERIC_MAX_DISPLAY_SCALE) + { + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * Approximate number of significant digits in the result. Note that the + * underflow test above means that this is necessarily >= 0. + */ + sig_digits = 1 + rscale + (int) f; + + /* + * The multiplications to produce the result may introduce an error of up + * to around log10(abs(exp)) digits, so work with this many extra digits + * of precision (plus a few more for good measure). + */ + sig_digits += (int) log(Abs(exp)) + 8; + + /* + * Now we can proceed with the multiplications. */ neg = (exp < 0); mask = Abs(exp); - local_rscale = rscale + MUL_GUARD_DIGITS * 2; - init_var(&base_prod); set_var_from_var(base, &base_prod); @@ -7105,9 +7237,27 @@ power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale) while ((mask >>= 1) > 0) { + /* + * Do the multiplications using rscales large enough to hold the + * results to the required number of significant digits, but don't + * waste time by exceeding the scales of the numbers themselves. + */ + local_rscale = sig_digits - 2 * base_prod.weight * DEC_DIGITS; + local_rscale = Min(local_rscale, 2 * base_prod.dscale); + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + mul_var(&base_prod, &base_prod, &base_prod, local_rscale); + if (mask & 1) + { + local_rscale = sig_digits - + (base_prod.weight + result->weight) * DEC_DIGITS; + local_rscale = Min(local_rscale, + base_prod.dscale + result->dscale); + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + mul_var(&base_prod, result, result, local_rscale); + } /* * When abs(base) > 1, the number of digits to the left of the decimal diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index c1886fd663..caac424744 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -1460,6 +1460,163 @@ select 10.0 ^ 2147483647 as overflows; ERROR: value overflows numeric format select 117743296169.0 ^ 1000000000 as overflows; ERROR: value overflows numeric format +-- cases that used to return inaccurate results +select 3.789 ^ 21; + ?column? +-------------------------------- + 1409343026052.8716016316022141 +(1 row) + +select 3.789 ^ 35; + ?column? +---------------------------------------- + 177158169650516670809.3820586142670135 +(1 row) + +select 1.2 ^ 345; + ?column? +----------------------------------------------- + 2077446682327378559843444695.5827049735727869 +(1 row) + +select 0.12 ^ (-20); + ?column? +-------------------------------------- + 2608405330458882702.5529619561355838 +(1 row) + +-- cases that used to error out +select 0.12 ^ (-25); + ?column? +------------------------------------------- + 104825960103961013959336.4983657883169110 +(1 row) + +select 0.5678 ^ (-85); + ?column? +---------------------------------------- + 782333637740774446257.7719390061997396 +(1 row) + +-- +-- Tests for raising to non-integer powers +-- +-- special cases +select 0.0 ^ 0.0; + ?column? +-------------------- + 1.0000000000000000 +(1 row) + +select (-12.34) ^ 0.0; + ?column? +-------------------- + 1.0000000000000000 +(1 row) + +select 12.34 ^ 0.0; + ?column? +-------------------- + 1.0000000000000000 +(1 row) + +select 0.0 ^ 12.34; + ?column? +-------------------- + 0.0000000000000000 +(1 row) + +-- invalid inputs +select 0.0 ^ (-12.34); +ERROR: zero raised to a negative power is undefined +select (-12.34) ^ 1.2; +ERROR: a negative number raised to a non-integer power yields a complex result +-- cases that used to generate inaccurate results +select 32.1 ^ 9.8; + ?column? +-------------------- + 580429286790711.10 +(1 row) + +select 32.1 ^ (-9.8); + ?column? +---------------------------------- + 0.000000000000001722862754788209 +(1 row) + +select 12.3 ^ 45.6; + ?column? +------------------------------------------------------ + 50081010321492803393171165777624533697036806969694.9 +(1 row) + +select 12.3 ^ (-45.6); + ?column? +--------------------------------------------------------------------- + 0.00000000000000000000000000000000000000000000000001996764828785491 +(1 row) + +-- big test +select 1.234 ^ 5678; + ?column? +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 307239295662090741644584872593956173493568238595074141254349565406661439636598896798876823220904084953233015553994854875890890858118656468658643918169805277399402542281777901029346337707622181574346585989613344285010764501017625366742865066948856161360224801370482171458030533346309750557140549621313515752078638620714732831815297168231790779296290266207315344008883935010274044001522606235576584215999260117523114297033944018699691024106823438431754073086813382242140602291215149759520833200152654884259619588924545324.5973362312547382 +(1 row) + +-- +-- Tests for EXP() +-- +-- special cases +select exp(0.0); + exp +-------------------- + 1.0000000000000000 +(1 row) + +select exp(1.0); + exp +-------------------- + 2.7182818284590452 +(1 row) + +select exp(1.0::numeric(71,70)); + exp +-------------------------------------------------------------------------- + 2.7182818284590452353602874713526624977572470936999595749669676277240766 +(1 row) + +-- cases that used to generate inaccurate results +select exp(32.999); + exp +--------------------- + 214429043492155.053 +(1 row) + +select exp(-32.999); + exp +---------------------------------- + 0.000000000000004663547361468248 +(1 row) + +select exp(123.456); + exp +------------------------------------------------------------ + 413294435277809344957685441227343146614594393746575438.725 +(1 row) + +select exp(-123.456); + exp +------------------------------------------------------------------------- + 0.000000000000000000000000000000000000000000000000000002419582541264601 +(1 row) + +-- big test +select exp(1234.5678); + exp +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 146549072930959479983482138503979804217622199675223653966270157446954995433819741094410764947112047906012815540251009949604426069672532417736057033099274204598385314594846509975629046864798765888104789074984927709616261452461385220475510438783429612447831614003668421849727379202555580791042606170523016207262965336641214601082882495255771621327088265411334088968112458492660609809762865582162764292604697957813514621259353683899630997077707406305730694385703091201347848855199354307506425820147289848677003277208302716466011827836279231.9667 +(1 row) + -- -- Tests for generate_series -- @@ -1550,3 +1707,148 @@ select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,i) 3 | 4 (10 rows) +-- +-- Tests for LN() +-- +-- Invalid inputs +select ln(-12.34); +ERROR: cannot take logarithm of a negative number +select ln(0.0); +ERROR: cannot take logarithm of zero +-- Some random tests +select ln(1.2345678e-28); + ln +----------------------------------------- + -64.26166165451762991204894255882820859 +(1 row) + +select ln(0.0456789); + ln +--------------------- + -3.0861187944847439 +(1 row) + +select ln(0.349873948359354029493948309745709580730482050975); + ln +----------------------------------------------------- + -1.050182336912082775693991697979750253056317885460 +(1 row) + +select ln(0.99949452); + ln +------------------------- + -0.00050560779808326467 +(1 row) + +select ln(1.00049687395); + ln +------------------------ + 0.00049675054901370394 +(1 row) + +select ln(1234.567890123456789); + ln +-------------------- + 7.1184763012977896 +(1 row) + +select ln(5.80397490724e5); + ln +-------------------- + 13.271468476626518 +(1 row) + +select ln(9.342536355e34); + ln +-------------------- + 80.522470935524187 +(1 row) + +-- +-- Tests for LOG() (base 10) +-- +-- invalid inputs +select log(-12.34); +ERROR: cannot take logarithm of a negative number +CONTEXT: SQL function "log" statement 1 +select log(0.0); +ERROR: cannot take logarithm of zero +CONTEXT: SQL function "log" statement 1 +-- some random tests +select log(1.234567e-89); + log +----------------------------------------------------------------------------------------------------- + -88.90848533591373725637496492944925187293052336306443143312825869985819779294142441287021741054275 +(1 row) + +select log(3.4634998359873254962349856073435545); + log +-------------------------------------- + 0.5395151714070134409152404011959981 +(1 row) + +select log(9.999999999999999999); + log +---------------------- + 1.000000000000000000 +(1 row) + +select log(10.00000000000000000); + log +--------------------- + 1.00000000000000000 +(1 row) + +select log(10.00000000000000001); + log +--------------------- + 1.00000000000000000 +(1 row) + +select log(590489.45235237); + log +------------------- + 5.771212144411727 +(1 row) + +-- +-- Tests for LOG() (arbitrary base) +-- +-- invalid inputs +select log(-12.34, 56.78); +ERROR: cannot take logarithm of a negative number +select log(-12.34, -56.78); +ERROR: cannot take logarithm of a negative number +select log(12.34, -56.78); +ERROR: cannot take logarithm of a negative number +select log(0.0, 12.34); +ERROR: cannot take logarithm of zero +select log(12.34, 0.0); +ERROR: cannot take logarithm of zero +select log(1.0, 12.34); +ERROR: division by zero +-- some random tests +select log(1.23e-89, 6.4689e45); + log +------------------------------------------------------------------------------------------------ + -0.5152489207781856983977054971756484879653568168479201885425588841094788842469115325262329756 +(1 row) + +select log(0.99923, 4.58934e34); + log +--------------------- + -103611.55579544132 +(1 row) + +select log(1.000016, 8.452010e18); + log +-------------------- + 2723830.2877097365 +(1 row) + +select log(3.1954752e47, 9.4792021e-73); + log +------------------------------------------------------------------------------------- + -1.51613372350688302142917386143459361608600157692779164475351842333265418126982165 +(1 row) + diff --git a/src/test/regress/expected/numeric_big.out b/src/test/regress/expected/numeric_big.out index efab469f54..1b3608ad53 100644 --- a/src/test/regress/expected/numeric_big.out +++ b/src/test/regress/expected/numeric_big.out @@ -671,3 +671,1379 @@ SELECT t1.id1, t1.result, t2.expected -----+--------+---------- (0 rows) +-- +-- Test code path for raising to integer powers +-- +-- base less than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for p in {-20..20} +-- do +-- b="0.084738" +-- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) +-- echo "($b, $p, $r)," +-- done +WITH t(b, p, bc_result) AS (VALUES +(0.084738, -20, 2744326694304960114888.7859130502035257), +(0.084738, -19, 232548755422013710215.4459407000481464), +(0.084738, -18, 19705716436950597776.2364581230406798), +(0.084738, -17, 1669822999434319754.3627249884302211), +(0.084738, -16, 141497461326065387.3451885900696001), +(0.084738, -15, 11990211877848128.7928565907453178), +(0.084738, -14, 1016026574105094.7376490817865767), +(0.084738, -13, 86096059836517.5178789078924309), +(0.084738, -12, 7295607918426.8214300228969888), +(0.084738, -11, 618215223791.6519943372802450), +(0.084738, -10, 52386321633.6570066961524534), +(0.084738, -9, 4439112122.5928274334185666), +(0.084738, -8, 376161483.0442710110530225), +(0.084738, -7, 31875171.7502054369346110), +(0.084738, -6, 2701038.3037689083149651), +(0.084738, -5, 228880.5837847697527935), +(0.084738, -4, 19394.8829087538193122), +(0.084738, -3, 1643.4835879219811409), +(0.084738, -2, 139.2655122733328379), +(0.084738, -1, 11.8010809790176780), +(0.084738, 0, 1), +(0.084738, 1, .084738), +(0.084738, 2, .007180528644), +(0.084738, 3, .0006084636362353), +(0.084738, 4, .0000515599916073), +(0.084738, 5, .0000043690905688), +(0.084738, 6, .0000003702279966), +(0.084738, 7, .0000000313723800), +(0.084738, 8, .0000000026584327), +(0.084738, 9, .0000000002252703), +(0.084738, 10, .0000000000190890), +(0.084738, 11, .0000000000016176), +(0.084738, 12, .0000000000001371), +(0.084738, 13, .0000000000000116), +(0.084738, 14, .0000000000000010), +(0.084738, 15, .0000000000000001), +(0.084738, 16, .0000000000000000), +(0.084738, 17, .0000000000000000), +(0.084738, 18, .0000000000000000), +(0.084738, 19, .0000000000000000), +(0.084738, 20, .0000000000000000)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + b | p | bc_result | power | diff +----------+-----+-----------------------------------------+-----------------------------------------+-------------------- + 0.084738 | -20 | 2744326694304960114888.7859130502035257 | 2744326694304960114888.7859130502035257 | 0.0000000000000000 + 0.084738 | -19 | 232548755422013710215.4459407000481464 | 232548755422013710215.4459407000481464 | 0.0000000000000000 + 0.084738 | -18 | 19705716436950597776.2364581230406798 | 19705716436950597776.2364581230406798 | 0.0000000000000000 + 0.084738 | -17 | 1669822999434319754.3627249884302211 | 1669822999434319754.3627249884302211 | 0.0000000000000000 + 0.084738 | -16 | 141497461326065387.3451885900696001 | 141497461326065387.3451885900696001 | 0.0000000000000000 + 0.084738 | -15 | 11990211877848128.7928565907453178 | 11990211877848128.7928565907453178 | 0.0000000000000000 + 0.084738 | -14 | 1016026574105094.7376490817865767 | 1016026574105094.7376490817865767 | 0.0000000000000000 + 0.084738 | -13 | 86096059836517.5178789078924309 | 86096059836517.5178789078924309 | 0.0000000000000000 + 0.084738 | -12 | 7295607918426.8214300228969888 | 7295607918426.8214300228969888 | 0.0000000000000000 + 0.084738 | -11 | 618215223791.6519943372802450 | 618215223791.6519943372802450 | 0.0000000000000000 + 0.084738 | -10 | 52386321633.6570066961524534 | 52386321633.6570066961524534 | 0.0000000000000000 + 0.084738 | -9 | 4439112122.5928274334185666 | 4439112122.5928274334185666 | 0.0000000000000000 + 0.084738 | -8 | 376161483.0442710110530225 | 376161483.0442710110530225 | 0.0000000000000000 + 0.084738 | -7 | 31875171.7502054369346110 | 31875171.7502054369346110 | 0.0000000000000000 + 0.084738 | -6 | 2701038.3037689083149651 | 2701038.3037689083149651 | 0.0000000000000000 + 0.084738 | -5 | 228880.5837847697527935 | 228880.5837847697527935 | 0.0000000000000000 + 0.084738 | -4 | 19394.8829087538193122 | 19394.8829087538193122 | 0.0000000000000000 + 0.084738 | -3 | 1643.4835879219811409 | 1643.4835879219811409 | 0.0000000000000000 + 0.084738 | -2 | 139.2655122733328379 | 139.2655122733328379 | 0.0000000000000000 + 0.084738 | -1 | 11.8010809790176780 | 11.8010809790176780 | 0.0000000000000000 + 0.084738 | 0 | 1 | 1.0000000000000000 | 0.0000000000000000 + 0.084738 | 1 | 0.084738 | 0.0847380000000000 | 0.0000000000000000 + 0.084738 | 2 | 0.007180528644 | 0.0071805286440000 | 0.0000000000000000 + 0.084738 | 3 | 0.0006084636362353 | 0.0006084636362353 | 0.0000000000000000 + 0.084738 | 4 | 0.0000515599916073 | 0.0000515599916073 | 0.0000000000000000 + 0.084738 | 5 | 0.0000043690905688 | 0.0000043690905688 | 0.0000000000000000 + 0.084738 | 6 | 0.0000003702279966 | 0.0000003702279966 | 0.0000000000000000 + 0.084738 | 7 | 0.0000000313723800 | 0.0000000313723800 | 0.0000000000000000 + 0.084738 | 8 | 0.0000000026584327 | 0.0000000026584327 | 0.0000000000000000 + 0.084738 | 9 | 0.0000000002252703 | 0.0000000002252703 | 0.0000000000000000 + 0.084738 | 10 | 0.0000000000190890 | 0.0000000000190890 | 0.0000000000000000 + 0.084738 | 11 | 0.0000000000016176 | 0.0000000000016176 | 0.0000000000000000 + 0.084738 | 12 | 0.0000000000001371 | 0.0000000000001371 | 0.0000000000000000 + 0.084738 | 13 | 0.0000000000000116 | 0.0000000000000116 | 0.0000000000000000 + 0.084738 | 14 | 0.0000000000000010 | 0.0000000000000010 | 0.0000000000000000 + 0.084738 | 15 | 0.0000000000000001 | 0.0000000000000001 | 0.0000000000000000 + 0.084738 | 16 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 0.084738 | 17 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 0.084738 | 18 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 0.084738 | 19 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 0.084738 | 20 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 +(41 rows) + +-- base greater than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for p in {-20..20} +-- do +-- b="37.821637" +-- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) +-- echo "($b, $p, $r)," +-- done +WITH t(b, p, bc_result) AS (VALUES +(37.821637, -20, .0000000000000000), +(37.821637, -19, .0000000000000000), +(37.821637, -18, .0000000000000000), +(37.821637, -17, .0000000000000000), +(37.821637, -16, .0000000000000000), +(37.821637, -15, .0000000000000000), +(37.821637, -14, .0000000000000000), +(37.821637, -13, .0000000000000000), +(37.821637, -12, .0000000000000000), +(37.821637, -11, .0000000000000000), +(37.821637, -10, .0000000000000002), +(37.821637, -9, .0000000000000063), +(37.821637, -8, .0000000000002388), +(37.821637, -7, .0000000000090327), +(37.821637, -6, .0000000003416316), +(37.821637, -5, .0000000129210673), +(37.821637, -4, .0000004886959182), +(37.821637, -3, .0000184832796213), +(37.821637, -2, .0006990678924066), +(37.821637, -1, .0264398920649574), +(37.821637, 0, 1), +(37.821637, 1, 37.821637), +(37.821637, 2, 1430.476225359769), +(37.821637, 3, 54102.9525326873775219), +(37.821637, 4, 2046262.2313195326271135), +(37.821637, 5, 77392987.3197773940323425), +(37.821637, 6, 2927129472.7542235178972258), +(37.821637, 7, 110708828370.5116321107718772), +(37.821637, 8, 4187189119324.7924539711577286), +(37.821637, 9, 158366346921451.9852944363360812), +(37.821637, 10, 5989674486279224.5007355092228730), +(37.821637, 11, 226539294168214309.7083246628376531), +(37.821637, 12, 8568086950266418559.9938312759931069), +(37.821637, 13, 324059074417413536066.1494087598581043), +(37.821637, 14, 12256444679171401239980.3109258799733927), +(37.821637, 15, 463558801566202198479885.2069857662592280), +(37.821637, 16, 17532552720991931019508170.1002855156233684), +(37.821637, 17, 663109844696719094948877928.0672523682648687), +(37.821637, 18, 25079899837245684700124994552.6717306599041850), +(37.821637, 19, 948562867640665366544581398598.1275771806665398), +(37.821637, 20, 35876200451584291931921101974730.6901038166532866)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + b | p | bc_result | power | diff +-----------+-----+---------------------------------------------------+---------------------------------------------------+-------------------- + 37.821637 | -20 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -19 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -18 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -17 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -16 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -15 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -14 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -13 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -12 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -11 | 0.0000000000000000 | 0.0000000000000000 | 0.0000000000000000 + 37.821637 | -10 | 0.0000000000000002 | 0.0000000000000002 | 0.0000000000000000 + 37.821637 | -9 | 0.0000000000000063 | 0.0000000000000063 | 0.0000000000000000 + 37.821637 | -8 | 0.0000000000002388 | 0.0000000000002388 | 0.0000000000000000 + 37.821637 | -7 | 0.0000000000090327 | 0.0000000000090327 | 0.0000000000000000 + 37.821637 | -6 | 0.0000000003416316 | 0.0000000003416316 | 0.0000000000000000 + 37.821637 | -5 | 0.0000000129210673 | 0.0000000129210673 | 0.0000000000000000 + 37.821637 | -4 | 0.0000004886959182 | 0.0000004886959182 | 0.0000000000000000 + 37.821637 | -3 | 0.0000184832796213 | 0.0000184832796213 | 0.0000000000000000 + 37.821637 | -2 | 0.0006990678924066 | 0.0006990678924066 | 0.0000000000000000 + 37.821637 | -1 | 0.0264398920649574 | 0.0264398920649574 | 0.0000000000000000 + 37.821637 | 0 | 1 | 1.0000000000000000 | 0.0000000000000000 + 37.821637 | 1 | 37.821637 | 37.8216370000000000 | 0.0000000000000000 + 37.821637 | 2 | 1430.476225359769 | 1430.4762253597690000 | 0.0000000000000000 + 37.821637 | 3 | 54102.9525326873775219 | 54102.9525326873775219 | 0.0000000000000000 + 37.821637 | 4 | 2046262.2313195326271135 | 2046262.2313195326271135 | 0.0000000000000000 + 37.821637 | 5 | 77392987.3197773940323425 | 77392987.3197773940323425 | 0.0000000000000000 + 37.821637 | 6 | 2927129472.7542235178972258 | 2927129472.7542235178972258 | 0.0000000000000000 + 37.821637 | 7 | 110708828370.5116321107718772 | 110708828370.5116321107718772 | 0.0000000000000000 + 37.821637 | 8 | 4187189119324.7924539711577286 | 4187189119324.7924539711577286 | 0.0000000000000000 + 37.821637 | 9 | 158366346921451.9852944363360812 | 158366346921451.9852944363360812 | 0.0000000000000000 + 37.821637 | 10 | 5989674486279224.5007355092228730 | 5989674486279224.5007355092228730 | 0.0000000000000000 + 37.821637 | 11 | 226539294168214309.7083246628376531 | 226539294168214309.7083246628376531 | 0.0000000000000000 + 37.821637 | 12 | 8568086950266418559.9938312759931069 | 8568086950266418559.9938312759931069 | 0.0000000000000000 + 37.821637 | 13 | 324059074417413536066.1494087598581043 | 324059074417413536066.1494087598581043 | 0.0000000000000000 + 37.821637 | 14 | 12256444679171401239980.3109258799733927 | 12256444679171401239980.3109258799733927 | 0.0000000000000000 + 37.821637 | 15 | 463558801566202198479885.2069857662592280 | 463558801566202198479885.2069857662592280 | 0.0000000000000000 + 37.821637 | 16 | 17532552720991931019508170.1002855156233684 | 17532552720991931019508170.1002855156233684 | 0.0000000000000000 + 37.821637 | 17 | 663109844696719094948877928.0672523682648687 | 663109844696719094948877928.0672523682648687 | 0.0000000000000000 + 37.821637 | 18 | 25079899837245684700124994552.6717306599041850 | 25079899837245684700124994552.6717306599041850 | 0.0000000000000000 + 37.821637 | 19 | 948562867640665366544581398598.1275771806665398 | 948562867640665366544581398598.1275771806665398 | 0.0000000000000000 + 37.821637 | 20 | 35876200451584291931921101974730.6901038166532866 | 35876200451584291931921101974730.6901038166532866 | 0.0000000000000000 +(41 rows) + +-- +-- Tests for raising to non-integer powers +-- +-- base less than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for n in {-20..20} +-- do +-- b="0.06933247" +-- p="$n.342987" +-- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) +-- echo "($b, $p, $r)," +-- done +WITH t(b, p, bc_result) AS (VALUES +(0.06933247, -20.342987, 379149253615977128356318.39406340), +(0.06933247, -19.342987, 26287354251852125772450.59436685), +(0.06933247, -18.342987, 1822567200045909954554.65766042), +(0.06933247, -17.342987, 126363085720167050546.86216560), +(0.06933247, -16.342987, 8761064849800910427.02880469), +(0.06933247, -15.342987, 607426265866876128.15466179), +(0.06933247, -14.342987, 42114363355427213.14899924), +(0.06933247, -13.342987, 2919892833909256.59283660), +(0.06933247, -12.342987, 202443382310228.51544515), +(0.06933247, -11.342987, 14035899730722.44924025), +(0.06933247, -10.342987, 973143597003.32229028), +(0.06933247, -9.342987, 67470449244.92493259), +(0.06933247, -8.342987, 4677892898.16028054), +(0.06933247, -7.342987, 324329869.02491071), +(0.06933247, -6.342987, 22486590.914273551), +(0.06933247, -5.342987, 1559050.8899661435), +(0.06933247, -4.342987, 108092.84905705095), +(0.06933247, -3.342987, 7494.3442144625131), +(0.06933247, -2.342987, 519.60139541889576), +(0.06933247, -1.342987, 36.025248159838727), +(0.06933247, 0.342987, .40036522320023350), +(0.06933247, 1.342987, .02775830982657349), +(0.06933247, 2.342987, .001924552183301612), +(0.06933247, 3.342987, .0001334339565121935), +(0.06933247, 4.342987, .000009251305786862961), +(0.06933247, 5.342987, .0000006414158809285026), +(0.06933247, 6.342987, .00000004447094732199898), +(0.06933247, 7.342987, .000000003083280621074075), +(0.06933247, 8.342987, .0000000002137714611621997), +(0.06933247, 9.342987, .00000000001482130341788437), +(0.06933247, 10.342987, .000000000001027597574581366), +(0.06933247, 11.342987, .00000000000007124587801173530), +(0.06933247, 12.342987, .000000000000004939652699872298), +(0.06933247, 13.342987, .0000000000000003424783226243151), +(0.06933247, 14.342987, .00000000000000002374486802900065), +(0.06933247, 15.342987, .000000000000000001646290350274646), +(0.06933247, 16.342987, .0000000000000000001141413763217064), +(0.06933247, 17.342987, .000000000000000000007913703549583420), +(0.06933247, 18.342987, .0000000000000000000005486766139403860), +(0.06933247, 19.342987, .00000000000000000000003804110487572339), +(0.06933247, 20.342987, .000000000000000000000002637483762562946)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + b | p | bc_result | power | diff +------------+------------+-------------------------------------------+-------------------------------------------+------------------------------------------- + 0.06933247 | -20.342987 | 379149253615977128356318.39406340 | 379149253615977128356318.39406340 | 0.00000000 + 0.06933247 | -19.342987 | 26287354251852125772450.59436685 | 26287354251852125772450.59436685 | 0.00000000 + 0.06933247 | -18.342987 | 1822567200045909954554.65766042 | 1822567200045909954554.65766042 | 0.00000000 + 0.06933247 | -17.342987 | 126363085720167050546.86216560 | 126363085720167050546.86216560 | 0.00000000 + 0.06933247 | -16.342987 | 8761064849800910427.02880469 | 8761064849800910427.02880469 | 0.00000000 + 0.06933247 | -15.342987 | 607426265866876128.15466179 | 607426265866876128.15466179 | 0.00000000 + 0.06933247 | -14.342987 | 42114363355427213.14899924 | 42114363355427213.14899924 | 0.00000000 + 0.06933247 | -13.342987 | 2919892833909256.59283660 | 2919892833909256.59283660 | 0.00000000 + 0.06933247 | -12.342987 | 202443382310228.51544515 | 202443382310228.51544515 | 0.00000000 + 0.06933247 | -11.342987 | 14035899730722.44924025 | 14035899730722.44924025 | 0.00000000 + 0.06933247 | -10.342987 | 973143597003.32229028 | 973143597003.32229028 | 0.00000000 + 0.06933247 | -9.342987 | 67470449244.92493259 | 67470449244.92493259 | 0.00000000 + 0.06933247 | -8.342987 | 4677892898.16028054 | 4677892898.16028054 | 0.00000000 + 0.06933247 | -7.342987 | 324329869.02491071 | 324329869.02491071 | 0.00000000 + 0.06933247 | -6.342987 | 22486590.914273551 | 22486590.914273551 | 0.000000000 + 0.06933247 | -5.342987 | 1559050.8899661435 | 1559050.8899661435 | 0.0000000000 + 0.06933247 | -4.342987 | 108092.84905705095 | 108092.84905705095 | 0.00000000000 + 0.06933247 | -3.342987 | 7494.3442144625131 | 7494.3442144625131 | 0.0000000000000 + 0.06933247 | -2.342987 | 519.60139541889576 | 519.60139541889576 | 0.00000000000000 + 0.06933247 | -1.342987 | 36.025248159838727 | 36.025248159838727 | 0.000000000000000 + 0.06933247 | 0.342987 | 0.40036522320023350 | 0.4003652232002335 | 0.00000000000000000 + 0.06933247 | 1.342987 | 0.02775830982657349 | 0.02775830982657349 | 0.00000000000000000 + 0.06933247 | 2.342987 | 0.001924552183301612 | 0.001924552183301612 | 0.000000000000000000 + 0.06933247 | 3.342987 | 0.0001334339565121935 | 0.0001334339565121935 | 0.0000000000000000000 + 0.06933247 | 4.342987 | 0.000009251305786862961 | 0.000009251305786862961 | 0.000000000000000000000 + 0.06933247 | 5.342987 | 0.0000006414158809285026 | 0.0000006414158809285026 | 0.0000000000000000000000 + 0.06933247 | 6.342987 | 0.00000004447094732199898 | 0.00000004447094732199898 | 0.00000000000000000000000 + 0.06933247 | 7.342987 | 0.000000003083280621074075 | 0.000000003083280621074075 | 0.000000000000000000000000 + 0.06933247 | 8.342987 | 0.0000000002137714611621997 | 0.0000000002137714611621997 | 0.0000000000000000000000000 + 0.06933247 | 9.342987 | 0.00000000001482130341788437 | 0.00000000001482130341788437 | 0.00000000000000000000000000 + 0.06933247 | 10.342987 | 0.000000000001027597574581366 | 0.000000000001027597574581366 | 0.000000000000000000000000000 + 0.06933247 | 11.342987 | 0.00000000000007124587801173530 | 0.00000000000007124587801173530 | 0.00000000000000000000000000000 + 0.06933247 | 12.342987 | 0.000000000000004939652699872298 | 0.000000000000004939652699872298 | 0.000000000000000000000000000000 + 0.06933247 | 13.342987 | 0.0000000000000003424783226243151 | 0.0000000000000003424783226243151 | 0.0000000000000000000000000000000 + 0.06933247 | 14.342987 | 0.00000000000000002374486802900065 | 0.00000000000000002374486802900065 | 0.00000000000000000000000000000000 + 0.06933247 | 15.342987 | 0.000000000000000001646290350274646 | 0.000000000000000001646290350274646 | 0.000000000000000000000000000000000 + 0.06933247 | 16.342987 | 0.0000000000000000001141413763217064 | 0.0000000000000000001141413763217064 | 0.0000000000000000000000000000000000 + 0.06933247 | 17.342987 | 0.000000000000000000007913703549583420 | 0.000000000000000000007913703549583420 | 0.000000000000000000000000000000000000 + 0.06933247 | 18.342987 | 0.0000000000000000000005486766139403860 | 0.0000000000000000000005486766139403860 | 0.0000000000000000000000000000000000000 + 0.06933247 | 19.342987 | 0.00000000000000000000003804110487572339 | 0.00000000000000000000003804110487572339 | 0.00000000000000000000000000000000000000 + 0.06933247 | 20.342987 | 0.000000000000000000000002637483762562946 | 0.000000000000000000000002637483762562946 | 0.000000000000000000000000000000000000000 +(41 rows) + +-- base greater than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for n in {-20..20} +-- do +-- b="27.234987" +-- p="$n.230957" +-- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) +-- echo "($b, $p, $r)," +-- done +WITH t(b, p, bc_result) AS (VALUES +(27.234987, -20.230957, .000000000000000000000000000009247064512095633), +(27.234987, -19.230957, .0000000000000000000000000002518436817750859), +(27.234987, -18.230957, .000000000000000000000000006858959399176602), +(27.234987, -17.230957, .0000000000000000000000001868036700701026), +(27.234987, -16.230957, .000000000000000000000005087595525911532), +(27.234987, -15.230957, .0000000000000000000001385605980094587), +(27.234987, -14.230957, .000000000000000000003773696085499835), +(27.234987, -13.230957, .0000000000000000001027765638305389), +(27.234987, -12.230957, .000000000000000002799118379829397), +(27.234987, -11.230957, .00000000000000007623395268611469), +(27.234987, -10.230957, .000000000000002076230710364949), +(27.234987, -9.230957, .00000000000005654611640579014), +(27.234987, -8.230957, .000000000001540032745212181), +(27.234987, -7.230957, .00000000004194277179542807), +(27.234987, -6.230957, .000000001142310844592450), +(27.234987, -5.230957, .00000003111082100243440), +(27.234987, -4.230957, .0000008473028055606278), +(27.234987, -3.230957, .00002307628089450723), +(27.234987, -2.230957, .0006284822101702527), +(27.234987, -1.230957, .01711670482371810), +(27.234987, 0.230957, 2.1451253063142300), +(27.234987, 1.230957, 58.422459830839071), +(27.234987, 2.230957, 1591.1349340009243), +(27.234987, 3.230957, 43334.539242761031), +(27.234987, 4.230957, 1180215.6129275865), +(27.234987, 5.230957, 32143156.875279851), +(27.234987, 6.230957, 875418459.63720737), +(27.234987, 7.230957, 23842010367.779367), +(27.234987, 8.230957, 649336842420.336290), +(27.234987, 9.230957, 17684680461938.907402), +(27.234987, 10.230957, 481642042480060.137900), +(27.234987, 11.230957, 13117514765597885.614921), +(27.234987, 12.230957, 357255344113366461.949871), +(27.234987, 13.230957, 9729844652608062117.440722), +(27.234987, 14.230957, 264992192625800087863.690528), +(27.234987, 15.230957, 7217058921265161257566.469315), +(27.234987, 16.230957, 196556505898890690402726.443417), +(27.234987, 17.230957, 5353213882921711267539279.451015), +(27.234987, 18.230957, 145794710509592328389185797.837767), +(27.234987, 19.230957, 3970717045397510438979206144.696206), +(27.234987, 20.230957, 108142427112079606637962972621.121293)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + b | p | bc_result | power | diff +-----------+------------+-------------------------------------------------+-------------------------------------------------+------------------------------------------------- + 27.234987 | -20.230957 | 0.000000000000000000000000000009247064512095633 | 0.000000000000000000000000000009247064512095633 | 0.000000000000000000000000000000000000000000000 + 27.234987 | -19.230957 | 0.0000000000000000000000000002518436817750859 | 0.0000000000000000000000000002518436817750859 | 0.0000000000000000000000000000000000000000000 + 27.234987 | -18.230957 | 0.000000000000000000000000006858959399176602 | 0.000000000000000000000000006858959399176602 | 0.000000000000000000000000000000000000000000 + 27.234987 | -17.230957 | 0.0000000000000000000000001868036700701026 | 0.0000000000000000000000001868036700701026 | 0.0000000000000000000000000000000000000000 + 27.234987 | -16.230957 | 0.000000000000000000000005087595525911532 | 0.000000000000000000000005087595525911532 | 0.000000000000000000000000000000000000000 + 27.234987 | -15.230957 | 0.0000000000000000000001385605980094587 | 0.0000000000000000000001385605980094587 | 0.0000000000000000000000000000000000000 + 27.234987 | -14.230957 | 0.000000000000000000003773696085499835 | 0.000000000000000000003773696085499835 | 0.000000000000000000000000000000000000 + 27.234987 | -13.230957 | 0.0000000000000000001027765638305389 | 0.0000000000000000001027765638305389 | 0.0000000000000000000000000000000000 + 27.234987 | -12.230957 | 0.000000000000000002799118379829397 | 0.000000000000000002799118379829397 | 0.000000000000000000000000000000000 + 27.234987 | -11.230957 | 0.00000000000000007623395268611469 | 0.00000000000000007623395268611469 | 0.00000000000000000000000000000000 + 27.234987 | -10.230957 | 0.000000000000002076230710364949 | 0.000000000000002076230710364949 | 0.000000000000000000000000000000 + 27.234987 | -9.230957 | 0.00000000000005654611640579014 | 0.00000000000005654611640579014 | 0.00000000000000000000000000000 + 27.234987 | -8.230957 | 0.000000000001540032745212181 | 0.000000000001540032745212181 | 0.000000000000000000000000000 + 27.234987 | -7.230957 | 0.00000000004194277179542807 | 0.00000000004194277179542807 | 0.00000000000000000000000000 + 27.234987 | -6.230957 | 0.000000001142310844592450 | 0.000000001142310844592450 | 0.000000000000000000000000 + 27.234987 | -5.230957 | 0.00000003111082100243440 | 0.00000003111082100243440 | 0.00000000000000000000000 + 27.234987 | -4.230957 | 0.0000008473028055606278 | 0.0000008473028055606278 | 0.0000000000000000000000 + 27.234987 | -3.230957 | 0.00002307628089450723 | 0.00002307628089450723 | 0.00000000000000000000 + 27.234987 | -2.230957 | 0.0006284822101702527 | 0.0006284822101702527 | 0.0000000000000000000 + 27.234987 | -1.230957 | 0.01711670482371810 | 0.01711670482371810 | 0.00000000000000000 + 27.234987 | 0.230957 | 2.1451253063142300 | 2.1451253063142300 | 0.0000000000000000 + 27.234987 | 1.230957 | 58.422459830839071 | 58.422459830839071 | 0.000000000000000 + 27.234987 | 2.230957 | 1591.1349340009243 | 1591.1349340009243 | 0.0000000000000 + 27.234987 | 3.230957 | 43334.539242761031 | 43334.539242761031 | 0.000000000000 + 27.234987 | 4.230957 | 1180215.6129275865 | 1180215.6129275865 | 0.0000000000 + 27.234987 | 5.230957 | 32143156.875279851 | 32143156.875279851 | 0.000000000 + 27.234987 | 6.230957 | 875418459.63720737 | 875418459.63720737 | 0.00000000 + 27.234987 | 7.230957 | 23842010367.779367 | 23842010367.779367 | 0.000000 + 27.234987 | 8.230957 | 649336842420.336290 | 649336842420.336290 | 0.000000 + 27.234987 | 9.230957 | 17684680461938.907402 | 17684680461938.907402 | 0.000000 + 27.234987 | 10.230957 | 481642042480060.137900 | 481642042480060.137900 | 0.000000 + 27.234987 | 11.230957 | 13117514765597885.614921 | 13117514765597885.614921 | 0.000000 + 27.234987 | 12.230957 | 357255344113366461.949871 | 357255344113366461.949871 | 0.000000 + 27.234987 | 13.230957 | 9729844652608062117.440722 | 9729844652608062117.440722 | 0.000000 + 27.234987 | 14.230957 | 264992192625800087863.690528 | 264992192625800087863.690528 | 0.000000 + 27.234987 | 15.230957 | 7217058921265161257566.469315 | 7217058921265161257566.469315 | 0.000000 + 27.234987 | 16.230957 | 196556505898890690402726.443417 | 196556505898890690402726.443417 | 0.000000 + 27.234987 | 17.230957 | 5353213882921711267539279.451015 | 5353213882921711267539279.451015 | 0.000000 + 27.234987 | 18.230957 | 145794710509592328389185797.837767 | 145794710509592328389185797.837767 | 0.000000 + 27.234987 | 19.230957 | 3970717045397510438979206144.696206 | 3970717045397510438979206144.696206 | 0.000000 + 27.234987 | 20.230957 | 108142427112079606637962972621.121293 | 108142427112079606637962972621.121293 | 0.000000 +(41 rows) + +-- +-- Tests for EXP() +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of EXP(): +-- +-- for n in {-20..20} +-- do +-- x="$n.29837" +-- r=$(bc -ql <<< "scale=500 ; e($x)" | head -n 1) +-- echo "($x, $r)," +-- done +WITH t(x, bc_result) AS (VALUES +(-20.29837, .000000001529431101152222), +(-19.29837, .000000004157424770142192), +(-18.29837, .00000001130105220586304), +(-17.29837, .00000003071944485366452), +(-16.29837, .00000008350410872606600), +(-15.29837, .0000002269877013517336), +(-14.29837, .0000006170165438681061), +(-13.29837, .000001677224859055276), +(-12.29837, .000004559169856609741), +(-11.29837, .00001239310857408049), +(-10.29837, .00003368796183504298), +(-9.29837, .00009157337449401917), +(-8.29837, .0002489222398577673), +(-7.29837, .0006766408013046928), +(-6.29837, .001839300394580514), +(-5.29837, .004999736839665763), +(-4.29837, .01359069379834070), +(-3.29837, .03694333598818056), +(-2.29837, .1004223988993283), +(-1.29837, .2729763820983097), +(0.29837, 1.3476603299656679), +(1.29837, 3.6633205858807959), +(2.29837, 9.9579377804197108), +(3.29837, 27.068481317440698), +(4.29837, 73.579760889182206), +(5.29837, 200.01052696742555), +(6.29837, 543.68498095607070), +(7.29837, 1477.8890041389891), +(8.29837, 4017.3188244304487), +(9.29837, 10920.204759575742), +(10.29837, 29684.194161006717), +(11.29837, 80690.005580314652), +(12.29837, 219338.17590722828), +(13.29837, 596222.97785597218), +(14.29837, 1620702.0864156289), +(15.29837, 4405525.0308492653), +(16.29837, 11975458.636179032), +(17.29837, 32552671.598188404), +(18.29837, 88487335.673150406), +(19.29837, 240533516.60908059), +(20.29837, 653837887.33381570)) +SELECT x, bc_result, exp(x), exp(x)-bc_result AS diff FROM t; + x | bc_result | exp | diff +-----------+----------------------------+----------------------------+---------------------------- + -20.29837 | 0.000000001529431101152222 | 0.000000001529431101152222 | 0.000000000000000000000000 + -19.29837 | 0.000000004157424770142192 | 0.000000004157424770142192 | 0.000000000000000000000000 + -18.29837 | 0.00000001130105220586304 | 0.00000001130105220586304 | 0.00000000000000000000000 + -17.29837 | 0.00000003071944485366452 | 0.00000003071944485366452 | 0.00000000000000000000000 + -16.29837 | 0.00000008350410872606600 | 0.00000008350410872606600 | 0.00000000000000000000000 + -15.29837 | 0.0000002269877013517336 | 0.0000002269877013517336 | 0.0000000000000000000000 + -14.29837 | 0.0000006170165438681061 | 0.0000006170165438681061 | 0.0000000000000000000000 + -13.29837 | 0.000001677224859055276 | 0.000001677224859055276 | 0.000000000000000000000 + -12.29837 | 0.000004559169856609741 | 0.000004559169856609741 | 0.000000000000000000000 + -11.29837 | 0.00001239310857408049 | 0.00001239310857408049 | 0.00000000000000000000 + -10.29837 | 0.00003368796183504298 | 0.00003368796183504298 | 0.00000000000000000000 + -9.29837 | 0.00009157337449401917 | 0.00009157337449401917 | 0.00000000000000000000 + -8.29837 | 0.0002489222398577673 | 0.0002489222398577673 | 0.0000000000000000000 + -7.29837 | 0.0006766408013046928 | 0.0006766408013046928 | 0.0000000000000000000 + -6.29837 | 0.001839300394580514 | 0.001839300394580514 | 0.000000000000000000 + -5.29837 | 0.004999736839665763 | 0.004999736839665763 | 0.000000000000000000 + -4.29837 | 0.01359069379834070 | 0.01359069379834070 | 0.00000000000000000 + -3.29837 | 0.03694333598818056 | 0.03694333598818056 | 0.00000000000000000 + -2.29837 | 0.1004223988993283 | 0.1004223988993283 | 0.0000000000000000 + -1.29837 | 0.2729763820983097 | 0.2729763820983097 | 0.0000000000000000 + 0.29837 | 1.3476603299656679 | 1.3476603299656679 | 0.0000000000000000 + 1.29837 | 3.6633205858807959 | 3.6633205858807959 | 0.0000000000000000 + 2.29837 | 9.9579377804197108 | 9.9579377804197108 | 0.0000000000000000 + 3.29837 | 27.068481317440698 | 27.068481317440698 | 0.000000000000000 + 4.29837 | 73.579760889182206 | 73.579760889182206 | 0.000000000000000 + 5.29837 | 200.01052696742555 | 200.01052696742555 | 0.00000000000000 + 6.29837 | 543.68498095607070 | 543.68498095607070 | 0.00000000000000 + 7.29837 | 1477.8890041389891 | 1477.8890041389891 | 0.0000000000000 + 8.29837 | 4017.3188244304487 | 4017.3188244304487 | 0.0000000000000 + 9.29837 | 10920.204759575742 | 10920.204759575742 | 0.000000000000 + 10.29837 | 29684.194161006717 | 29684.194161006717 | 0.000000000000 + 11.29837 | 80690.005580314652 | 80690.005580314652 | 0.000000000000 + 12.29837 | 219338.17590722828 | 219338.17590722828 | 0.00000000000 + 13.29837 | 596222.97785597218 | 596222.97785597218 | 0.00000000000 + 14.29837 | 1620702.0864156289 | 1620702.0864156289 | 0.0000000000 + 15.29837 | 4405525.0308492653 | 4405525.0308492653 | 0.0000000000 + 16.29837 | 11975458.636179032 | 11975458.636179032 | 0.000000000 + 17.29837 | 32552671.598188404 | 32552671.598188404 | 0.000000000 + 18.29837 | 88487335.673150406 | 88487335.673150406 | 0.000000000 + 19.29837 | 240533516.60908059 | 240533516.60908059 | 0.00000000 + 20.29837 | 653837887.33381570 | 653837887.33381570 | 0.00000000 +(41 rows) + +-- +-- Tests for LN() +-- +-- input very small +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done +WITH t(x, bc_result) AS (VALUES +('1.0e-1', -2.3025850929940457), +('1.0e-2', -4.6051701859880914), +('1.0e-3', -6.9077552789821371), +('1.0e-4', -9.2103403719761827), +('1.0e-5', -11.512925464970228), +('1.0e-6', -13.815510557964274), +('1.0e-7', -16.118095650958320), +('1.0e-8', -18.420680743952365), +('1.0e-9', -20.723265836946411), +('1.0e-10', -23.025850929940457), +('1.0e-11', -25.328436022934503), +('1.0e-12', -27.631021115928548), +('1.0e-13', -29.933606208922594), +('1.0e-14', -32.236191301916640), +('1.0e-15', -34.5387763949106853), +('1.0e-16', -36.84136148790473094), +('1.0e-17', -39.143946580898776628), +('1.0e-18', -41.4465316738928223123), +('1.0e-19', -43.74911676688686799634), +('1.0e-20', -46.051701859880913680360), +('1.0e-21', -48.3542869528749593643778), +('1.0e-22', -50.65687204586900504839581), +('1.0e-23', -52.959457138863050732413803), +('1.0e-24', -55.2620422318570964164317949), +('1.0e-25', -57.56462732485114210044978637), +('1.0e-26', -59.867212417845187784467777822), +('1.0e-27', -62.1697975108392334684857692765), +('1.0e-28', -64.47238260383327915250376073116), +('1.0e-29', -66.774967696827324836521752185847), +('1.0e-30', -69.0775527898213705205397436405309), +('1.0e-31', -71.38013788281541620455773509521529), +('1.0e-32', -73.682722975809461888575726549899655), +('1.0e-33', -75.9853080688035075725937180045840189), +('1.0e-34', -78.28789316179755325661170945926838306), +('1.0e-35', -80.590478254791598940629700913952747266), +('1.0e-36', -82.8930633477856446246476923686371114736), +('1.0e-37', -85.19564844077969030866568382332147568124), +('1.0e-38', -87.498233533773735992683675278005839888842), +('1.0e-39', -89.8008186267677816767016667326902040964430), +('1.0e-40', -92.10340371976182736071965818737456830404406)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + x | bc_result | ln | diff +---------+-----------------------------------------------+-----------------------------------------------+--------------------------------------------- + 1.0e-1 | -2.3025850929940457 | -2.3025850929940457 | 0.0000000000000000 + 1.0e-2 | -4.6051701859880914 | -4.6051701859880914 | 0.0000000000000000 + 1.0e-3 | -6.9077552789821371 | -6.9077552789821371 | 0.0000000000000000 + 1.0e-4 | -9.2103403719761827 | -9.2103403719761827 | 0.0000000000000000 + 1.0e-5 | -11.512925464970228 | -11.512925464970228 | 0.000000000000000 + 1.0e-6 | -13.815510557964274 | -13.815510557964274 | 0.000000000000000 + 1.0e-7 | -16.118095650958320 | -16.118095650958320 | 0.000000000000000 + 1.0e-8 | -18.420680743952365 | -18.420680743952365 | 0.000000000000000 + 1.0e-9 | -20.723265836946411 | -20.723265836946411 | 0.000000000000000 + 1.0e-10 | -23.025850929940457 | -23.025850929940457 | 0.000000000000000 + 1.0e-11 | -25.328436022934503 | -25.328436022934503 | 0.000000000000000 + 1.0e-12 | -27.631021115928548 | -27.631021115928548 | 0.000000000000000 + 1.0e-13 | -29.933606208922594 | -29.933606208922594 | 0.000000000000000 + 1.0e-14 | -32.236191301916640 | -32.236191301916640 | 0.000000000000000 + 1.0e-15 | -34.5387763949106853 | -34.5387763949106853 | 0.0000000000000000 + 1.0e-16 | -36.84136148790473094 | -36.84136148790473094 | 0.00000000000000000 + 1.0e-17 | -39.143946580898776628 | -39.143946580898776628 | 0.000000000000000000 + 1.0e-18 | -41.4465316738928223123 | -41.4465316738928223123 | 0.0000000000000000000 + 1.0e-19 | -43.74911676688686799634 | -43.74911676688686799634 | 0.00000000000000000000 + 1.0e-20 | -46.051701859880913680360 | -46.051701859880913680360 | 0.000000000000000000000 + 1.0e-21 | -48.3542869528749593643778 | -48.3542869528749593643778 | 0.0000000000000000000000 + 1.0e-22 | -50.65687204586900504839581 | -50.65687204586900504839581 | 0.00000000000000000000000 + 1.0e-23 | -52.959457138863050732413803 | -52.959457138863050732413803 | 0.000000000000000000000000 + 1.0e-24 | -55.2620422318570964164317949 | -55.2620422318570964164317949 | 0.0000000000000000000000000 + 1.0e-25 | -57.56462732485114210044978637 | -57.56462732485114210044978637 | 0.00000000000000000000000000 + 1.0e-26 | -59.867212417845187784467777822 | -59.867212417845187784467777822 | 0.000000000000000000000000000 + 1.0e-27 | -62.1697975108392334684857692765 | -62.1697975108392334684857692765 | 0.0000000000000000000000000000 + 1.0e-28 | -64.47238260383327915250376073116 | -64.47238260383327915250376073116 | 0.00000000000000000000000000000 + 1.0e-29 | -66.774967696827324836521752185847 | -66.774967696827324836521752185847 | 0.000000000000000000000000000000 + 1.0e-30 | -69.0775527898213705205397436405309 | -69.0775527898213705205397436405309 | 0.0000000000000000000000000000000 + 1.0e-31 | -71.38013788281541620455773509521529 | -71.38013788281541620455773509521529 | 0.00000000000000000000000000000000 + 1.0e-32 | -73.682722975809461888575726549899655 | -73.682722975809461888575726549899655 | 0.000000000000000000000000000000000 + 1.0e-33 | -75.9853080688035075725937180045840189 | -75.9853080688035075725937180045840189 | 0.0000000000000000000000000000000000 + 1.0e-34 | -78.28789316179755325661170945926838306 | -78.28789316179755325661170945926838306 | 0.00000000000000000000000000000000000 + 1.0e-35 | -80.590478254791598940629700913952747266 | -80.590478254791598940629700913952747266 | 0.000000000000000000000000000000000000 + 1.0e-36 | -82.8930633477856446246476923686371114736 | -82.8930633477856446246476923686371114736 | 0.0000000000000000000000000000000000000 + 1.0e-37 | -85.19564844077969030866568382332147568124 | -85.19564844077969030866568382332147568124 | 0.00000000000000000000000000000000000000 + 1.0e-38 | -87.498233533773735992683675278005839888842 | -87.498233533773735992683675278005839888842 | 0.000000000000000000000000000000000000000 + 1.0e-39 | -89.8008186267677816767016667326902040964430 | -89.8008186267677816767016667326902040964430 | 0.0000000000000000000000000000000000000000 + 1.0e-40 | -92.10340371976182736071965818737456830404406 | -92.10340371976182736071965818737456830404406 | 0.00000000000000000000000000000000000000000 +(40 rows) + +-- input very close to but smaller than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1-10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done +WITH t(x, bc_result) AS (VALUES +('1.0e-1', -.10536051565782630), +('1.0e-2', -.010050335853501441), +('1.0e-3', -.0010005003335835335), +('1.0e-4', -.00010000500033335834), +('1.0e-5', -.000010000050000333336), +('1.0e-6', -.0000010000005000003333), +('1.0e-7', -.00000010000000500000033), +('1.0e-8', -.000000010000000050000000), +('1.0e-9', -.0000000010000000005000000), +('1.0e-10', -.00000000010000000000500000), +('1.0e-11', -.000000000010000000000050000), +('1.0e-12', -.0000000000010000000000005000), +('1.0e-13', -.00000000000010000000000000500), +('1.0e-14', -.000000000000010000000000000050), +('1.0e-15', -.0000000000000010000000000000005), +('1.0e-16', -.00000000000000010000000000000001), +('1.0e-17', -.000000000000000010000000000000000), +('1.0e-18', -.0000000000000000010000000000000000), +('1.0e-19', -.00000000000000000010000000000000000), +('1.0e-20', -.000000000000000000010000000000000000), +('1.0e-21', -.0000000000000000000010000000000000000), +('1.0e-22', -.00000000000000000000010000000000000000), +('1.0e-23', -.000000000000000000000010000000000000000), +('1.0e-24', -.0000000000000000000000010000000000000000), +('1.0e-25', -.00000000000000000000000010000000000000000), +('1.0e-26', -.000000000000000000000000010000000000000000), +('1.0e-27', -.0000000000000000000000000010000000000000000), +('1.0e-28', -.00000000000000000000000000010000000000000000), +('1.0e-29', -.000000000000000000000000000010000000000000000), +('1.0e-30', -.0000000000000000000000000000010000000000000000), +('1.0e-31', -.00000000000000000000000000000010000000000000000), +('1.0e-32', -.000000000000000000000000000000010000000000000000), +('1.0e-33', -.0000000000000000000000000000000010000000000000000), +('1.0e-34', -.00000000000000000000000000000000010000000000000000), +('1.0e-35', -.000000000000000000000000000000000010000000000000000), +('1.0e-36', -.0000000000000000000000000000000000010000000000000000), +('1.0e-37', -.00000000000000000000000000000000000010000000000000000), +('1.0e-38', -.000000000000000000000000000000000000010000000000000000), +('1.0e-39', -.0000000000000000000000000000000000000010000000000000000), +('1.0e-40', -.00000000000000000000000000000000000000010000000000000000)) +SELECT '1-'||x, bc_result, ln(1.0-x::numeric), ln(1.0-x::numeric)-bc_result AS diff FROM t; + ?column? | bc_result | ln | diff +-----------+-------------------------------------------------------------+-------------------------------------------------------------+------------------------------------------------------------ + 1-1.0e-1 | -0.10536051565782630 | -0.10536051565782630 | 0.00000000000000000 + 1-1.0e-2 | -0.010050335853501441 | -0.010050335853501441 | 0.000000000000000000 + 1-1.0e-3 | -0.0010005003335835335 | -0.0010005003335835335 | 0.0000000000000000000 + 1-1.0e-4 | -0.00010000500033335834 | -0.00010000500033335834 | 0.00000000000000000000 + 1-1.0e-5 | -0.000010000050000333336 | -0.000010000050000333336 | 0.000000000000000000000 + 1-1.0e-6 | -0.0000010000005000003333 | -0.0000010000005000003333 | 0.0000000000000000000000 + 1-1.0e-7 | -0.00000010000000500000033 | -0.00000010000000500000033 | 0.00000000000000000000000 + 1-1.0e-8 | -0.000000010000000050000000 | -0.000000010000000050000000 | 0.000000000000000000000000 + 1-1.0e-9 | -0.0000000010000000005000000 | -0.0000000010000000005000000 | 0.0000000000000000000000000 + 1-1.0e-10 | -0.00000000010000000000500000 | -0.00000000010000000000500000 | 0.00000000000000000000000000 + 1-1.0e-11 | -0.000000000010000000000050000 | -0.000000000010000000000050000 | 0.000000000000000000000000000 + 1-1.0e-12 | -0.0000000000010000000000005000 | -0.0000000000010000000000005000 | 0.0000000000000000000000000000 + 1-1.0e-13 | -0.00000000000010000000000000500 | -0.00000000000010000000000000500 | 0.00000000000000000000000000000 + 1-1.0e-14 | -0.000000000000010000000000000050 | -0.000000000000010000000000000050 | 0.000000000000000000000000000000 + 1-1.0e-15 | -0.0000000000000010000000000000005 | -0.0000000000000010000000000000005 | 0.0000000000000000000000000000000 + 1-1.0e-16 | -0.00000000000000010000000000000001 | -0.00000000000000010000000000000001 | 0.00000000000000000000000000000000 + 1-1.0e-17 | -0.000000000000000010000000000000000 | -0.000000000000000010000000000000000 | 0.000000000000000000000000000000000 + 1-1.0e-18 | -0.0000000000000000010000000000000000 | -0.0000000000000000010000000000000000 | 0.0000000000000000000000000000000000 + 1-1.0e-19 | -0.00000000000000000010000000000000000 | -0.00000000000000000010000000000000000 | 0.00000000000000000000000000000000000 + 1-1.0e-20 | -0.000000000000000000010000000000000000 | -0.000000000000000000010000000000000000 | 0.000000000000000000000000000000000000 + 1-1.0e-21 | -0.0000000000000000000010000000000000000 | -0.0000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000 + 1-1.0e-22 | -0.00000000000000000000010000000000000000 | -0.00000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000 + 1-1.0e-23 | -0.000000000000000000000010000000000000000 | -0.000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000 + 1-1.0e-24 | -0.0000000000000000000000010000000000000000 | -0.0000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000 + 1-1.0e-25 | -0.00000000000000000000000010000000000000000 | -0.00000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000 + 1-1.0e-26 | -0.000000000000000000000000010000000000000000 | -0.000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000 + 1-1.0e-27 | -0.0000000000000000000000000010000000000000000 | -0.0000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000 + 1-1.0e-28 | -0.00000000000000000000000000010000000000000000 | -0.00000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000 + 1-1.0e-29 | -0.000000000000000000000000000010000000000000000 | -0.000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000 + 1-1.0e-30 | -0.0000000000000000000000000000010000000000000000 | -0.0000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000 + 1-1.0e-31 | -0.00000000000000000000000000000010000000000000000 | -0.00000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000 + 1-1.0e-32 | -0.000000000000000000000000000000010000000000000000 | -0.000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000 + 1-1.0e-33 | -0.0000000000000000000000000000000010000000000000000 | -0.0000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000 + 1-1.0e-34 | -0.00000000000000000000000000000000010000000000000000 | -0.00000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000 + 1-1.0e-35 | -0.000000000000000000000000000000000010000000000000000 | -0.000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000000 + 1-1.0e-36 | -0.0000000000000000000000000000000000010000000000000000 | -0.0000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000000 + 1-1.0e-37 | -0.00000000000000000000000000000000000010000000000000000 | -0.00000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000000 + 1-1.0e-38 | -0.000000000000000000000000000000000000010000000000000000 | -0.000000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000000000 + 1-1.0e-39 | -0.0000000000000000000000000000000000000010000000000000000 | -0.0000000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000000000 + 1-1.0e-40 | -0.00000000000000000000000000000000000000010000000000000000 | -0.00000000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000000000 +(40 rows) + +-- input very close to but larger than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1+10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done +WITH t(x, bc_result) AS (VALUES +('1.0e-1', .09531017980432486), +('1.0e-2', .009950330853168083), +('1.0e-3', .0009995003330835332), +('1.0e-4', .00009999500033330834), +('1.0e-5', .000009999950000333331), +('1.0e-6', .0000009999995000003333), +('1.0e-7', .00000009999999500000033), +('1.0e-8', .000000009999999950000000), +('1.0e-9', .0000000009999999995000000), +('1.0e-10', .00000000009999999999500000), +('1.0e-11', .000000000009999999999950000), +('1.0e-12', .0000000000009999999999995000), +('1.0e-13', .00000000000009999999999999500), +('1.0e-14', .000000000000009999999999999950), +('1.0e-15', .0000000000000009999999999999995), +('1.0e-16', .00000000000000010000000000000000), +('1.0e-17', .000000000000000010000000000000000), +('1.0e-18', .0000000000000000010000000000000000), +('1.0e-19', .00000000000000000010000000000000000), +('1.0e-20', .000000000000000000010000000000000000), +('1.0e-21', .0000000000000000000010000000000000000), +('1.0e-22', .00000000000000000000010000000000000000), +('1.0e-23', .000000000000000000000010000000000000000), +('1.0e-24', .0000000000000000000000010000000000000000), +('1.0e-25', .00000000000000000000000010000000000000000), +('1.0e-26', .000000000000000000000000010000000000000000), +('1.0e-27', .0000000000000000000000000010000000000000000), +('1.0e-28', .00000000000000000000000000010000000000000000), +('1.0e-29', .000000000000000000000000000010000000000000000), +('1.0e-30', .0000000000000000000000000000010000000000000000), +('1.0e-31', .00000000000000000000000000000010000000000000000), +('1.0e-32', .000000000000000000000000000000010000000000000000), +('1.0e-33', .0000000000000000000000000000000010000000000000000), +('1.0e-34', .00000000000000000000000000000000010000000000000000), +('1.0e-35', .000000000000000000000000000000000010000000000000000), +('1.0e-36', .0000000000000000000000000000000000010000000000000000), +('1.0e-37', .00000000000000000000000000000000000010000000000000000), +('1.0e-38', .000000000000000000000000000000000000010000000000000000), +('1.0e-39', .0000000000000000000000000000000000000010000000000000000), +('1.0e-40', .00000000000000000000000000000000000000010000000000000000)) +SELECT '1+'||x, bc_result, ln(1.0+x::numeric), ln(1.0+x::numeric)-bc_result AS diff FROM t; + ?column? | bc_result | ln | diff +-----------+------------------------------------------------------------+------------------------------------------------------------+------------------------------------------------------------ + 1+1.0e-1 | 0.09531017980432486 | 0.09531017980432486 | 0.00000000000000000 + 1+1.0e-2 | 0.009950330853168083 | 0.009950330853168083 | 0.000000000000000000 + 1+1.0e-3 | 0.0009995003330835332 | 0.0009995003330835332 | 0.0000000000000000000 + 1+1.0e-4 | 0.00009999500033330834 | 0.00009999500033330834 | 0.00000000000000000000 + 1+1.0e-5 | 0.000009999950000333331 | 0.000009999950000333331 | 0.000000000000000000000 + 1+1.0e-6 | 0.0000009999995000003333 | 0.0000009999995000003333 | 0.0000000000000000000000 + 1+1.0e-7 | 0.00000009999999500000033 | 0.00000009999999500000033 | 0.00000000000000000000000 + 1+1.0e-8 | 0.000000009999999950000000 | 0.000000009999999950000000 | 0.000000000000000000000000 + 1+1.0e-9 | 0.0000000009999999995000000 | 0.0000000009999999995000000 | 0.0000000000000000000000000 + 1+1.0e-10 | 0.00000000009999999999500000 | 0.00000000009999999999500000 | 0.00000000000000000000000000 + 1+1.0e-11 | 0.000000000009999999999950000 | 0.000000000009999999999950000 | 0.000000000000000000000000000 + 1+1.0e-12 | 0.0000000000009999999999995000 | 0.0000000000009999999999995000 | 0.0000000000000000000000000000 + 1+1.0e-13 | 0.00000000000009999999999999500 | 0.00000000000009999999999999500 | 0.00000000000000000000000000000 + 1+1.0e-14 | 0.000000000000009999999999999950 | 0.000000000000009999999999999950 | 0.000000000000000000000000000000 + 1+1.0e-15 | 0.0000000000000009999999999999995 | 0.0000000000000009999999999999995 | 0.0000000000000000000000000000000 + 1+1.0e-16 | 0.00000000000000010000000000000000 | 0.00000000000000010000000000000000 | 0.00000000000000000000000000000000 + 1+1.0e-17 | 0.000000000000000010000000000000000 | 0.000000000000000010000000000000000 | 0.000000000000000000000000000000000 + 1+1.0e-18 | 0.0000000000000000010000000000000000 | 0.0000000000000000010000000000000000 | 0.0000000000000000000000000000000000 + 1+1.0e-19 | 0.00000000000000000010000000000000000 | 0.00000000000000000010000000000000000 | 0.00000000000000000000000000000000000 + 1+1.0e-20 | 0.000000000000000000010000000000000000 | 0.000000000000000000010000000000000000 | 0.000000000000000000000000000000000000 + 1+1.0e-21 | 0.0000000000000000000010000000000000000 | 0.0000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000 + 1+1.0e-22 | 0.00000000000000000000010000000000000000 | 0.00000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000 + 1+1.0e-23 | 0.000000000000000000000010000000000000000 | 0.000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000 + 1+1.0e-24 | 0.0000000000000000000000010000000000000000 | 0.0000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000 + 1+1.0e-25 | 0.00000000000000000000000010000000000000000 | 0.00000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000 + 1+1.0e-26 | 0.000000000000000000000000010000000000000000 | 0.000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000 + 1+1.0e-27 | 0.0000000000000000000000000010000000000000000 | 0.0000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000 + 1+1.0e-28 | 0.00000000000000000000000000010000000000000000 | 0.00000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000 + 1+1.0e-29 | 0.000000000000000000000000000010000000000000000 | 0.000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000 + 1+1.0e-30 | 0.0000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000 + 1+1.0e-31 | 0.00000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000 + 1+1.0e-32 | 0.000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000 + 1+1.0e-33 | 0.0000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000 + 1+1.0e-34 | 0.00000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000 + 1+1.0e-35 | 0.000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000000 + 1+1.0e-36 | 0.0000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000000 + 1+1.0e-37 | 0.00000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000000 + 1+1.0e-38 | 0.000000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000010000000000000000 | 0.000000000000000000000000000000000000000000000000000000 + 1+1.0e-39 | 0.0000000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000010000000000000000 | 0.0000000000000000000000000000000000000000000000000000000 + 1+1.0e-40 | 0.00000000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000010000000000000000 | 0.00000000000000000000000000000000000000000000000000000000 +(40 rows) + +-- input very large +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(10^$p)" | head -n 1) +-- echo "('1.0e$p', $l)," +-- done +WITH t(x, bc_result) AS (VALUES +('1.0e1', 2.3025850929940457), +('1.0e2', 4.6051701859880914), +('1.0e3', 6.9077552789821371), +('1.0e4', 9.2103403719761827), +('1.0e5', 11.512925464970228), +('1.0e6', 13.815510557964274), +('1.0e7', 16.118095650958320), +('1.0e8', 18.420680743952365), +('1.0e9', 20.723265836946411), +('1.0e10', 23.025850929940457), +('1.0e11', 25.328436022934503), +('1.0e12', 27.631021115928548), +('1.0e13', 29.933606208922594), +('1.0e14', 32.236191301916640), +('1.0e15', 34.538776394910685), +('1.0e16', 36.841361487904731), +('1.0e17', 39.143946580898777), +('1.0e18', 41.446531673892822), +('1.0e19', 43.749116766886868), +('1.0e20', 46.051701859880914), +('1.0e21', 48.354286952874959), +('1.0e22', 50.656872045869005), +('1.0e23', 52.959457138863051), +('1.0e24', 55.262042231857096), +('1.0e25', 57.564627324851142), +('1.0e26', 59.867212417845188), +('1.0e27', 62.169797510839233), +('1.0e28', 64.472382603833279), +('1.0e29', 66.774967696827325), +('1.0e30', 69.077552789821371), +('1.0e31', 71.380137882815416), +('1.0e32', 73.682722975809462), +('1.0e33', 75.985308068803508), +('1.0e34', 78.287893161797553), +('1.0e35', 80.590478254791599), +('1.0e36', 82.893063347785645), +('1.0e37', 85.195648440779690), +('1.0e38', 87.498233533773736), +('1.0e39', 89.800818626767782), +('1.0e40', 92.103403719761827)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + x | bc_result | ln | diff +--------+--------------------+--------------------+-------------------- + 1.0e1 | 2.3025850929940457 | 2.3025850929940457 | 0.0000000000000000 + 1.0e2 | 4.6051701859880914 | 4.6051701859880914 | 0.0000000000000000 + 1.0e3 | 6.9077552789821371 | 6.9077552789821371 | 0.0000000000000000 + 1.0e4 | 9.2103403719761827 | 9.2103403719761827 | 0.0000000000000000 + 1.0e5 | 11.512925464970228 | 11.512925464970228 | 0.000000000000000 + 1.0e6 | 13.815510557964274 | 13.815510557964274 | 0.000000000000000 + 1.0e7 | 16.118095650958320 | 16.118095650958320 | 0.000000000000000 + 1.0e8 | 18.420680743952365 | 18.420680743952365 | 0.000000000000000 + 1.0e9 | 20.723265836946411 | 20.723265836946411 | 0.000000000000000 + 1.0e10 | 23.025850929940457 | 23.025850929940457 | 0.000000000000000 + 1.0e11 | 25.328436022934503 | 25.328436022934503 | 0.000000000000000 + 1.0e12 | 27.631021115928548 | 27.631021115928548 | 0.000000000000000 + 1.0e13 | 29.933606208922594 | 29.933606208922594 | 0.000000000000000 + 1.0e14 | 32.236191301916640 | 32.236191301916640 | 0.000000000000000 + 1.0e15 | 34.538776394910685 | 34.538776394910685 | 0.000000000000000 + 1.0e16 | 36.841361487904731 | 36.841361487904731 | 0.000000000000000 + 1.0e17 | 39.143946580898777 | 39.143946580898777 | 0.000000000000000 + 1.0e18 | 41.446531673892822 | 41.446531673892822 | 0.000000000000000 + 1.0e19 | 43.749116766886868 | 43.749116766886868 | 0.000000000000000 + 1.0e20 | 46.051701859880914 | 46.051701859880914 | 0.000000000000000 + 1.0e21 | 48.354286952874959 | 48.354286952874959 | 0.000000000000000 + 1.0e22 | 50.656872045869005 | 50.656872045869005 | 0.000000000000000 + 1.0e23 | 52.959457138863051 | 52.959457138863051 | 0.000000000000000 + 1.0e24 | 55.262042231857096 | 55.262042231857096 | 0.000000000000000 + 1.0e25 | 57.564627324851142 | 57.564627324851142 | 0.000000000000000 + 1.0e26 | 59.867212417845188 | 59.867212417845188 | 0.000000000000000 + 1.0e27 | 62.169797510839233 | 62.169797510839233 | 0.000000000000000 + 1.0e28 | 64.472382603833279 | 64.472382603833279 | 0.000000000000000 + 1.0e29 | 66.774967696827325 | 66.774967696827325 | 0.000000000000000 + 1.0e30 | 69.077552789821371 | 69.077552789821371 | 0.000000000000000 + 1.0e31 | 71.380137882815416 | 71.380137882815416 | 0.000000000000000 + 1.0e32 | 73.682722975809462 | 73.682722975809462 | 0.000000000000000 + 1.0e33 | 75.985308068803508 | 75.985308068803508 | 0.000000000000000 + 1.0e34 | 78.287893161797553 | 78.287893161797553 | 0.000000000000000 + 1.0e35 | 80.590478254791599 | 80.590478254791599 | 0.000000000000000 + 1.0e36 | 82.893063347785645 | 82.893063347785645 | 0.000000000000000 + 1.0e37 | 85.195648440779690 | 85.195648440779690 | 0.000000000000000 + 1.0e38 | 87.498233533773736 | 87.498233533773736 | 0.000000000000000 + 1.0e39 | 89.800818626767782 | 89.800818626767782 | 0.000000000000000 + 1.0e40 | 92.103403719761827 | 92.103403719761827 | 0.000000000000000 +(40 rows) + +-- input huge +-- +-- bc(1) results computed with a scale of 1000 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..10} +-- do +-- l=$(bc -ql <<< "scale=1000 ; l(10^${p}00)" | head -n 1) +-- echo "('1.0e${p}00', $l)," +-- done +WITH t(x, bc_result) AS (VALUES +('1.0e100', 230.25850929940457), +('1.0e200', 460.51701859880914), +('1.0e300', 690.77552789821371), +('1.0e400', 921.03403719761827), +('1.0e500', 1151.2925464970228), +('1.0e600', 1381.5510557964274), +('1.0e700', 1611.8095650958320), +('1.0e800', 1842.0680743952365), +('1.0e900', 2072.3265836946411), +('1.0e1000', 2302.5850929940457)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + x | bc_result | ln | diff +----------+--------------------+--------------------+------------------ + 1.0e100 | 230.25850929940457 | 230.25850929940457 | 0.00000000000000 + 1.0e200 | 460.51701859880914 | 460.51701859880914 | 0.00000000000000 + 1.0e300 | 690.77552789821371 | 690.77552789821371 | 0.00000000000000 + 1.0e400 | 921.03403719761827 | 921.03403719761827 | 0.00000000000000 + 1.0e500 | 1151.2925464970228 | 1151.2925464970228 | 0.0000000000000 + 1.0e600 | 1381.5510557964274 | 1381.5510557964274 | 0.0000000000000 + 1.0e700 | 1611.8095650958320 | 1611.8095650958320 | 0.0000000000000 + 1.0e800 | 1842.0680743952365 | 1842.0680743952365 | 0.0000000000000 + 1.0e900 | 2072.3265836946411 | 2072.3265836946411 | 0.0000000000000 + 1.0e1000 | 2302.5850929940457 | 2302.5850929940457 | 0.0000000000000 +(10 rows) + +-- +-- Tests for LOG() (base 10) +-- +-- input very small, exact result known +WITH t(x) AS (SELECT '1e-'||n FROM generate_series(1, 100) g(n)) +SELECT x, log(x::numeric) FROM t; + x | log +--------+----------------------------------------------------------------------------------------------------------- + 1e-1 | -1.0000000000000000 + 1e-2 | -2.0000000000000000 + 1e-3 | -3.0000000000000000 + 1e-4 | -4.0000000000000000 + 1e-5 | -5.000000000000000 + 1e-6 | -6.000000000000000 + 1e-7 | -7.000000000000000 + 1e-8 | -8.000000000000000 + 1e-9 | -9.000000000000000 + 1e-10 | -10.000000000000000 + 1e-11 | -11.000000000000000 + 1e-12 | -12.000000000000000 + 1e-13 | -13.000000000000000 + 1e-14 | -14.000000000000000 + 1e-15 | -15.000000000000000 + 1e-16 | -16.0000000000000000 + 1e-17 | -17.00000000000000000 + 1e-18 | -18.000000000000000000 + 1e-19 | -19.0000000000000000000 + 1e-20 | -20.00000000000000000000 + 1e-21 | -21.000000000000000000000 + 1e-22 | -22.0000000000000000000000 + 1e-23 | -23.00000000000000000000000 + 1e-24 | -24.000000000000000000000000 + 1e-25 | -25.0000000000000000000000000 + 1e-26 | -26.00000000000000000000000000 + 1e-27 | -27.000000000000000000000000000 + 1e-28 | -28.0000000000000000000000000000 + 1e-29 | -29.00000000000000000000000000000 + 1e-30 | -30.000000000000000000000000000000 + 1e-31 | -31.0000000000000000000000000000000 + 1e-32 | -32.00000000000000000000000000000000 + 1e-33 | -33.000000000000000000000000000000000 + 1e-34 | -34.0000000000000000000000000000000000 + 1e-35 | -35.00000000000000000000000000000000000 + 1e-36 | -36.000000000000000000000000000000000000 + 1e-37 | -37.0000000000000000000000000000000000000 + 1e-38 | -38.00000000000000000000000000000000000000 + 1e-39 | -39.000000000000000000000000000000000000000 + 1e-40 | -40.0000000000000000000000000000000000000000 + 1e-41 | -41.00000000000000000000000000000000000000000 + 1e-42 | -42.000000000000000000000000000000000000000000 + 1e-43 | -43.0000000000000000000000000000000000000000000 + 1e-44 | -44.00000000000000000000000000000000000000000000 + 1e-45 | -45.000000000000000000000000000000000000000000000 + 1e-46 | -46.0000000000000000000000000000000000000000000000 + 1e-47 | -47.00000000000000000000000000000000000000000000000 + 1e-48 | -48.000000000000000000000000000000000000000000000000 + 1e-49 | -49.0000000000000000000000000000000000000000000000000 + 1e-50 | -50.00000000000000000000000000000000000000000000000000 + 1e-51 | -51.000000000000000000000000000000000000000000000000000 + 1e-52 | -52.0000000000000000000000000000000000000000000000000000 + 1e-53 | -53.00000000000000000000000000000000000000000000000000000 + 1e-54 | -54.000000000000000000000000000000000000000000000000000000 + 1e-55 | -55.0000000000000000000000000000000000000000000000000000000 + 1e-56 | -56.00000000000000000000000000000000000000000000000000000000 + 1e-57 | -57.000000000000000000000000000000000000000000000000000000000 + 1e-58 | -58.0000000000000000000000000000000000000000000000000000000000 + 1e-59 | -59.00000000000000000000000000000000000000000000000000000000000 + 1e-60 | -60.000000000000000000000000000000000000000000000000000000000000 + 1e-61 | -61.0000000000000000000000000000000000000000000000000000000000000 + 1e-62 | -62.00000000000000000000000000000000000000000000000000000000000000 + 1e-63 | -63.000000000000000000000000000000000000000000000000000000000000000 + 1e-64 | -64.0000000000000000000000000000000000000000000000000000000000000000 + 1e-65 | -65.00000000000000000000000000000000000000000000000000000000000000000 + 1e-66 | -66.000000000000000000000000000000000000000000000000000000000000000000 + 1e-67 | -67.0000000000000000000000000000000000000000000000000000000000000000000 + 1e-68 | -68.00000000000000000000000000000000000000000000000000000000000000000000 + 1e-69 | -69.000000000000000000000000000000000000000000000000000000000000000000000 + 1e-70 | -70.0000000000000000000000000000000000000000000000000000000000000000000000 + 1e-71 | -71.00000000000000000000000000000000000000000000000000000000000000000000000 + 1e-72 | -72.000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-73 | -73.0000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-74 | -74.00000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-75 | -75.000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-76 | -76.0000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-77 | -77.00000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-78 | -78.000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-79 | -79.0000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-80 | -80.00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-81 | -81.000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-82 | -82.0000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-83 | -83.00000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-84 | -84.000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-85 | -85.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-86 | -86.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-87 | -87.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-88 | -88.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-89 | -89.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-90 | -90.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-91 | -91.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-92 | -92.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-93 | -93.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-94 | -94.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-95 | -95.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-96 | -96.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-97 | -97.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-98 | -98.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-99 | -99.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 1e-100 | -100.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +(100 rows) + +-- input very small, non-exact results +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..50..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l($d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done +WITH t(x, bc_result) AS (VALUES +('9.0e-1', -.04575749056067513), +('6.0e-1', -.2218487496163564), +('3.0e-1', -.5228787452803376), +('9.0e-8', -7.045757490560675), +('6.0e-8', -7.221848749616356), +('3.0e-8', -7.522878745280338), +('9.0e-15', -14.0457574905606751), +('6.0e-15', -14.2218487496163564), +('3.0e-15', -14.5228787452803376), +('9.0e-22', -21.04575749056067512540994), +('6.0e-22', -21.22184874961635636749123), +('3.0e-22', -21.52287874528033756270497), +('9.0e-29', -28.045757490560675125409944193490), +('6.0e-29', -28.221848749616356367491233202020), +('3.0e-29', -28.522878745280337562704972096745), +('9.0e-36', -35.0457574905606751254099441934897693816), +('6.0e-36', -35.2218487496163563674912332020203916640), +('3.0e-36', -35.5228787452803375627049720967448846908), +('9.0e-43', -42.04575749056067512540994419348976938159974227), +('6.0e-43', -42.22184874961635636749123320202039166403168125), +('3.0e-43', -42.52287874528033756270497209674488469079987114), +('9.0e-50', -49.045757490560675125409944193489769381599742271618608), +('6.0e-50', -49.221848749616356367491233202020391664031681254347196), +('3.0e-50', -49.522878745280337562704972096744884690799871135809304)) +SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t; + x | bc_result | log | diff +---------+---------------------------------------------------------+---------------------------------------------------------+------------------------------------------------------- + 9.0e-1 | -0.04575749056067513 | -0.04575749056067513 | 0.00000000000000000 + 6.0e-1 | -0.2218487496163564 | -0.2218487496163564 | 0.0000000000000000 + 3.0e-1 | -0.5228787452803376 | -0.5228787452803376 | 0.0000000000000000 + 9.0e-8 | -7.045757490560675 | -7.045757490560675 | 0.000000000000000 + 6.0e-8 | -7.221848749616356 | -7.221848749616356 | 0.000000000000000 + 3.0e-8 | -7.522878745280338 | -7.522878745280338 | 0.000000000000000 + 9.0e-15 | -14.0457574905606751 | -14.0457574905606751 | 0.0000000000000000 + 6.0e-15 | -14.2218487496163564 | -14.2218487496163564 | 0.0000000000000000 + 3.0e-15 | -14.5228787452803376 | -14.5228787452803376 | 0.0000000000000000 + 9.0e-22 | -21.04575749056067512540994 | -21.04575749056067512540994 | 0.00000000000000000000000 + 6.0e-22 | -21.22184874961635636749123 | -21.22184874961635636749123 | 0.00000000000000000000000 + 3.0e-22 | -21.52287874528033756270497 | -21.52287874528033756270497 | 0.00000000000000000000000 + 9.0e-29 | -28.045757490560675125409944193490 | -28.045757490560675125409944193490 | 0.000000000000000000000000000000 + 6.0e-29 | -28.221848749616356367491233202020 | -28.221848749616356367491233202020 | 0.000000000000000000000000000000 + 3.0e-29 | -28.522878745280337562704972096745 | -28.522878745280337562704972096745 | 0.000000000000000000000000000000 + 9.0e-36 | -35.0457574905606751254099441934897693816 | -35.0457574905606751254099441934897693816 | 0.0000000000000000000000000000000000000 + 6.0e-36 | -35.2218487496163563674912332020203916640 | -35.2218487496163563674912332020203916640 | 0.0000000000000000000000000000000000000 + 3.0e-36 | -35.5228787452803375627049720967448846908 | -35.5228787452803375627049720967448846908 | 0.0000000000000000000000000000000000000 + 9.0e-43 | -42.04575749056067512540994419348976938159974227 | -42.04575749056067512540994419348976938159974227 | 0.00000000000000000000000000000000000000000000 + 6.0e-43 | -42.22184874961635636749123320202039166403168125 | -42.22184874961635636749123320202039166403168125 | 0.00000000000000000000000000000000000000000000 + 3.0e-43 | -42.52287874528033756270497209674488469079987114 | -42.52287874528033756270497209674488469079987114 | 0.00000000000000000000000000000000000000000000 + 9.0e-50 | -49.045757490560675125409944193489769381599742271618608 | -49.045757490560675125409944193489769381599742271618608 | 0.000000000000000000000000000000000000000000000000000 + 6.0e-50 | -49.221848749616356367491233202020391664031681254347196 | -49.221848749616356367491233202020391664031681254347196 | 0.000000000000000000000000000000000000000000000000000 + 3.0e-50 | -49.522878745280337562704972096744884690799871135809304 | -49.522878745280337562704972096744884690799871135809304 | 0.000000000000000000000000000000000000000000000000000 +(24 rows) + +-- input very close to but smaller than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1-$d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done +WITH t(x, bc_result) AS (VALUES +('9.0e-1', -1.0000000000000000), +('6.0e-1', -.3979400086720376), +('3.0e-1', -.1549019599857432), +('9.0e-8', -.000000039086505130185422), +('6.0e-8', -.000000026057669695925208), +('3.0e-8', -.000000013028834652530076), +('9.0e-15', -.0000000000000039086503371292840), +('6.0e-15', -.0000000000000026057668914195188), +('3.0e-15', -.0000000000000013028834457097574), +('9.0e-22', -.00000000000000000000039086503371292664), +('6.0e-22', -.00000000000000000000026057668914195110), +('3.0e-22', -.00000000000000000000013028834457097555), +('9.0e-29', -.000000000000000000000000000039086503371292664), +('6.0e-29', -.000000000000000000000000000026057668914195110), +('3.0e-29', -.000000000000000000000000000013028834457097555), +('9.0e-36', -.0000000000000000000000000000000000039086503371292664), +('6.0e-36', -.0000000000000000000000000000000000026057668914195110), +('3.0e-36', -.0000000000000000000000000000000000013028834457097555)) +SELECT '1-'||x, bc_result, log(1.0-x::numeric), log(1.0-x::numeric)-bc_result AS diff FROM t; + ?column? | bc_result | log | diff +-----------+---------------------------------------------------------+---------------------------------------------------------+-------------------------------------------------------- + 1-9.0e-1 | -1.0000000000000000 | -1.0000000000000000 | 0.0000000000000000 + 1-6.0e-1 | -0.3979400086720376 | -0.3979400086720376 | 0.0000000000000000 + 1-3.0e-1 | -0.1549019599857432 | -0.1549019599857432 | 0.0000000000000000 + 1-9.0e-8 | -0.000000039086505130185422 | -0.000000039086505130185422 | 0.000000000000000000000000 + 1-6.0e-8 | -0.000000026057669695925208 | -0.000000026057669695925208 | 0.000000000000000000000000 + 1-3.0e-8 | -0.000000013028834652530076 | -0.000000013028834652530076 | 0.000000000000000000000000 + 1-9.0e-15 | -0.0000000000000039086503371292840 | -0.0000000000000039086503371292840 | 0.0000000000000000000000000000000 + 1-6.0e-15 | -0.0000000000000026057668914195188 | -0.0000000000000026057668914195188 | 0.0000000000000000000000000000000 + 1-3.0e-15 | -0.0000000000000013028834457097574 | -0.0000000000000013028834457097574 | 0.0000000000000000000000000000000 + 1-9.0e-22 | -0.00000000000000000000039086503371292664 | -0.00000000000000000000039086503371292664 | 0.00000000000000000000000000000000000000 + 1-6.0e-22 | -0.00000000000000000000026057668914195110 | -0.00000000000000000000026057668914195110 | 0.00000000000000000000000000000000000000 + 1-3.0e-22 | -0.00000000000000000000013028834457097555 | -0.00000000000000000000013028834457097555 | 0.00000000000000000000000000000000000000 + 1-9.0e-29 | -0.000000000000000000000000000039086503371292664 | -0.000000000000000000000000000039086503371292664 | 0.000000000000000000000000000000000000000000000 + 1-6.0e-29 | -0.000000000000000000000000000026057668914195110 | -0.000000000000000000000000000026057668914195110 | 0.000000000000000000000000000000000000000000000 + 1-3.0e-29 | -0.000000000000000000000000000013028834457097555 | -0.000000000000000000000000000013028834457097555 | 0.000000000000000000000000000000000000000000000 + 1-9.0e-36 | -0.0000000000000000000000000000000000039086503371292664 | -0.0000000000000000000000000000000000039086503371292664 | 0.0000000000000000000000000000000000000000000000000000 + 1-6.0e-36 | -0.0000000000000000000000000000000000026057668914195110 | -0.0000000000000000000000000000000000026057668914195110 | 0.0000000000000000000000000000000000000000000000000000 + 1-3.0e-36 | -0.0000000000000000000000000000000000013028834457097555 | -0.0000000000000000000000000000000000013028834457097555 | 0.0000000000000000000000000000000000000000000000000000 +(18 rows) + +-- input very close to but larger than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1+$d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done +WITH t(x, bc_result) AS (VALUES +('9.0e-1', .2787536009528290), +('6.0e-1', .2041199826559248), +('3.0e-1', .1139433523068368), +('9.0e-8', .000000039086501612400118), +('6.0e-8', .000000026057668132465074), +('3.0e-8', .000000013028834261665042), +('9.0e-15', .0000000000000039086503371292489), +('6.0e-15', .0000000000000026057668914195031), +('3.0e-15', .0000000000000013028834457097535), +('9.0e-22', .00000000000000000000039086503371292664), +('6.0e-22', .00000000000000000000026057668914195110), +('3.0e-22', .00000000000000000000013028834457097555), +('9.0e-29', .000000000000000000000000000039086503371292664), +('6.0e-29', .000000000000000000000000000026057668914195110), +('3.0e-29', .000000000000000000000000000013028834457097555), +('9.0e-36', .0000000000000000000000000000000000039086503371292664), +('6.0e-36', .0000000000000000000000000000000000026057668914195110), +('3.0e-36', .0000000000000000000000000000000000013028834457097555)) +SELECT '1+'||x, bc_result, log(1.0+x::numeric), log(1.0+x::numeric)-bc_result AS diff FROM t; + ?column? | bc_result | log | diff +-----------+--------------------------------------------------------+--------------------------------------------------------+-------------------------------------------------------- + 1+9.0e-1 | 0.2787536009528290 | 0.2787536009528290 | 0.0000000000000000 + 1+6.0e-1 | 0.2041199826559248 | 0.2041199826559248 | 0.0000000000000000 + 1+3.0e-1 | 0.1139433523068368 | 0.1139433523068368 | 0.0000000000000000 + 1+9.0e-8 | 0.000000039086501612400118 | 0.000000039086501612400118 | 0.000000000000000000000000 + 1+6.0e-8 | 0.000000026057668132465074 | 0.000000026057668132465074 | 0.000000000000000000000000 + 1+3.0e-8 | 0.000000013028834261665042 | 0.000000013028834261665042 | 0.000000000000000000000000 + 1+9.0e-15 | 0.0000000000000039086503371292489 | 0.0000000000000039086503371292489 | 0.0000000000000000000000000000000 + 1+6.0e-15 | 0.0000000000000026057668914195031 | 0.0000000000000026057668914195031 | 0.0000000000000000000000000000000 + 1+3.0e-15 | 0.0000000000000013028834457097535 | 0.0000000000000013028834457097535 | 0.0000000000000000000000000000000 + 1+9.0e-22 | 0.00000000000000000000039086503371292664 | 0.00000000000000000000039086503371292664 | 0.00000000000000000000000000000000000000 + 1+6.0e-22 | 0.00000000000000000000026057668914195110 | 0.00000000000000000000026057668914195110 | 0.00000000000000000000000000000000000000 + 1+3.0e-22 | 0.00000000000000000000013028834457097555 | 0.00000000000000000000013028834457097555 | 0.00000000000000000000000000000000000000 + 1+9.0e-29 | 0.000000000000000000000000000039086503371292664 | 0.000000000000000000000000000039086503371292664 | 0.000000000000000000000000000000000000000000000 + 1+6.0e-29 | 0.000000000000000000000000000026057668914195110 | 0.000000000000000000000000000026057668914195110 | 0.000000000000000000000000000000000000000000000 + 1+3.0e-29 | 0.000000000000000000000000000013028834457097555 | 0.000000000000000000000000000013028834457097555 | 0.000000000000000000000000000000000000000000000 + 1+9.0e-36 | 0.0000000000000000000000000000000000039086503371292664 | 0.0000000000000000000000000000000000039086503371292664 | 0.0000000000000000000000000000000000000000000000000000 + 1+6.0e-36 | 0.0000000000000000000000000000000000026057668914195110 | 0.0000000000000000000000000000000000026057668914195110 | 0.0000000000000000000000000000000000000000000000000000 + 1+3.0e-36 | 0.0000000000000000000000000000000000013028834457097555 | 0.0000000000000000000000000000000000013028834457097555 | 0.0000000000000000000000000000000000000000000000000000 +(18 rows) + +-- input very large, exact result known +WITH t(x) AS (SELECT '1e'||n FROM generate_series(1, 100) g(n)) +SELECT x, log(x::numeric) FROM t; + x | log +-------+-------------------- + 1e1 | 1.0000000000000000 + 1e2 | 2.0000000000000000 + 1e3 | 3.0000000000000000 + 1e4 | 4.0000000000000000 + 1e5 | 5.000000000000000 + 1e6 | 6.000000000000000 + 1e7 | 7.000000000000000 + 1e8 | 8.000000000000000 + 1e9 | 9.000000000000000 + 1e10 | 10.000000000000000 + 1e11 | 11.000000000000000 + 1e12 | 12.000000000000000 + 1e13 | 13.000000000000000 + 1e14 | 14.000000000000000 + 1e15 | 15.000000000000000 + 1e16 | 16.000000000000000 + 1e17 | 17.000000000000000 + 1e18 | 18.000000000000000 + 1e19 | 19.000000000000000 + 1e20 | 20.000000000000000 + 1e21 | 21.000000000000000 + 1e22 | 22.000000000000000 + 1e23 | 23.000000000000000 + 1e24 | 24.000000000000000 + 1e25 | 25.000000000000000 + 1e26 | 26.000000000000000 + 1e27 | 27.000000000000000 + 1e28 | 28.000000000000000 + 1e29 | 29.000000000000000 + 1e30 | 30.000000000000000 + 1e31 | 31.000000000000000 + 1e32 | 32.000000000000000 + 1e33 | 33.000000000000000 + 1e34 | 34.000000000000000 + 1e35 | 35.000000000000000 + 1e36 | 36.000000000000000 + 1e37 | 37.000000000000000 + 1e38 | 38.000000000000000 + 1e39 | 39.000000000000000 + 1e40 | 40.000000000000000 + 1e41 | 41.000000000000000 + 1e42 | 42.000000000000000 + 1e43 | 43.000000000000000 + 1e44 | 44.00000000000000 + 1e45 | 45.00000000000000 + 1e46 | 46.00000000000000 + 1e47 | 47.00000000000000 + 1e48 | 48.00000000000000 + 1e49 | 49.00000000000000 + 1e50 | 50.00000000000000 + 1e51 | 51.00000000000000 + 1e52 | 52.00000000000000 + 1e53 | 53.00000000000000 + 1e54 | 54.00000000000000 + 1e55 | 55.00000000000000 + 1e56 | 56.00000000000000 + 1e57 | 57.00000000000000 + 1e58 | 58.00000000000000 + 1e59 | 59.00000000000000 + 1e60 | 60.00000000000000 + 1e61 | 61.00000000000000 + 1e62 | 62.00000000000000 + 1e63 | 63.00000000000000 + 1e64 | 64.00000000000000 + 1e65 | 65.00000000000000 + 1e66 | 66.00000000000000 + 1e67 | 67.00000000000000 + 1e68 | 68.00000000000000 + 1e69 | 69.00000000000000 + 1e70 | 70.00000000000000 + 1e71 | 71.00000000000000 + 1e72 | 72.00000000000000 + 1e73 | 73.00000000000000 + 1e74 | 74.00000000000000 + 1e75 | 75.00000000000000 + 1e76 | 76.00000000000000 + 1e77 | 77.00000000000000 + 1e78 | 78.00000000000000 + 1e79 | 79.00000000000000 + 1e80 | 80.00000000000000 + 1e81 | 81.00000000000000 + 1e82 | 82.00000000000000 + 1e83 | 83.00000000000000 + 1e84 | 84.00000000000000 + 1e85 | 85.00000000000000 + 1e86 | 86.00000000000000 + 1e87 | 87.00000000000000 + 1e88 | 88.00000000000000 + 1e89 | 89.00000000000000 + 1e90 | 90.00000000000000 + 1e91 | 91.00000000000000 + 1e92 | 92.00000000000000 + 1e93 | 93.00000000000000 + 1e94 | 94.00000000000000 + 1e95 | 95.00000000000000 + 1e96 | 96.00000000000000 + 1e97 | 97.00000000000000 + 1e98 | 98.00000000000000 + 1e99 | 99.00000000000000 + 1e100 | 100.00000000000000 +(100 rows) + +-- input very large, non-exact results +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {10..50..7} +-- do +-- for d in {2..9..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l($d*10^$p) / l(10)" | head -n 1) +-- echo "('${d}.0e$p', $l)," +-- done +-- done +WITH t(x, bc_result) AS (VALUES +('2.0e10', 10.301029995663981), +('5.0e10', 10.698970004336019), +('8.0e10', 10.903089986991944), +('2.0e17', 17.301029995663981), +('5.0e17', 17.698970004336019), +('8.0e17', 17.903089986991944), +('2.0e24', 24.301029995663981), +('5.0e24', 24.698970004336019), +('8.0e24', 24.903089986991944), +('2.0e31', 31.301029995663981), +('5.0e31', 31.698970004336019), +('8.0e31', 31.903089986991944), +('2.0e38', 38.301029995663981), +('5.0e38', 38.698970004336019), +('8.0e38', 38.903089986991944), +('2.0e45', 45.30102999566398), +('5.0e45', 45.69897000433602), +('8.0e45', 45.90308998699194)) +SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t; + x | bc_result | log | diff +--------+--------------------+--------------------+------------------- + 2.0e10 | 10.301029995663981 | 10.301029995663981 | 0.000000000000000 + 5.0e10 | 10.698970004336019 | 10.698970004336019 | 0.000000000000000 + 8.0e10 | 10.903089986991944 | 10.903089986991944 | 0.000000000000000 + 2.0e17 | 17.301029995663981 | 17.301029995663981 | 0.000000000000000 + 5.0e17 | 17.698970004336019 | 17.698970004336019 | 0.000000000000000 + 8.0e17 | 17.903089986991944 | 17.903089986991944 | 0.000000000000000 + 2.0e24 | 24.301029995663981 | 24.301029995663981 | 0.000000000000000 + 5.0e24 | 24.698970004336019 | 24.698970004336019 | 0.000000000000000 + 8.0e24 | 24.903089986991944 | 24.903089986991944 | 0.000000000000000 + 2.0e31 | 31.301029995663981 | 31.301029995663981 | 0.000000000000000 + 5.0e31 | 31.698970004336019 | 31.698970004336019 | 0.000000000000000 + 8.0e31 | 31.903089986991944 | 31.903089986991944 | 0.000000000000000 + 2.0e38 | 38.301029995663981 | 38.301029995663981 | 0.000000000000000 + 5.0e38 | 38.698970004336019 | 38.698970004336019 | 0.000000000000000 + 8.0e38 | 38.903089986991944 | 38.903089986991944 | 0.000000000000000 + 2.0e45 | 45.30102999566398 | 45.30102999566398 | 0.00000000000000 + 5.0e45 | 45.69897000433602 | 45.69897000433602 | 0.00000000000000 + 8.0e45 | 45.90308998699194 | 45.90308998699194 | 0.00000000000000 +(18 rows) + diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index 49ec478bd9..0a4d1cb1d2 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -860,6 +860,57 @@ select 10.0 ^ -2147483647 as rounds_to_zero; select 10.0 ^ 2147483647 as overflows; select 117743296169.0 ^ 1000000000 as overflows; +-- cases that used to return inaccurate results +select 3.789 ^ 21; +select 3.789 ^ 35; +select 1.2 ^ 345; +select 0.12 ^ (-20); + +-- cases that used to error out +select 0.12 ^ (-25); +select 0.5678 ^ (-85); + +-- +-- Tests for raising to non-integer powers +-- + +-- special cases +select 0.0 ^ 0.0; +select (-12.34) ^ 0.0; +select 12.34 ^ 0.0; +select 0.0 ^ 12.34; + +-- invalid inputs +select 0.0 ^ (-12.34); +select (-12.34) ^ 1.2; + +-- cases that used to generate inaccurate results +select 32.1 ^ 9.8; +select 32.1 ^ (-9.8); +select 12.3 ^ 45.6; +select 12.3 ^ (-45.6); + +-- big test +select 1.234 ^ 5678; + +-- +-- Tests for EXP() +-- + +-- special cases +select exp(0.0); +select exp(1.0); +select exp(1.0::numeric(71,70)); + +-- cases that used to generate inaccurate results +select exp(32.999); +select exp(-32.999); +select exp(123.456); +select exp(-123.456); + +-- big test +select exp(1234.5678); + -- -- Tests for generate_series -- @@ -880,3 +931,55 @@ select (i / (10::numeric ^ 131071))::numeric(1,0) select * from generate_series(1::numeric, 3::numeric) i, generate_series(i,3) j; select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,i) j; select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,i) j; + +-- +-- Tests for LN() +-- + +-- Invalid inputs +select ln(-12.34); +select ln(0.0); + +-- Some random tests +select ln(1.2345678e-28); +select ln(0.0456789); +select ln(0.349873948359354029493948309745709580730482050975); +select ln(0.99949452); +select ln(1.00049687395); +select ln(1234.567890123456789); +select ln(5.80397490724e5); +select ln(9.342536355e34); + +-- +-- Tests for LOG() (base 10) +-- + +-- invalid inputs +select log(-12.34); +select log(0.0); + +-- some random tests +select log(1.234567e-89); +select log(3.4634998359873254962349856073435545); +select log(9.999999999999999999); +select log(10.00000000000000000); +select log(10.00000000000000001); +select log(590489.45235237); + +-- +-- Tests for LOG() (arbitrary base) +-- + +-- invalid inputs +select log(-12.34, 56.78); +select log(-12.34, -56.78); +select log(12.34, -56.78); +select log(0.0, 12.34); +select log(12.34, 0.0); +select log(1.0, 12.34); + +-- some random tests +select log(1.23e-89, 6.4689e45); +select log(0.99923, 4.58934e34); +select log(1.000016, 8.452010e18); +select log(3.1954752e47, 9.4792021e-73); diff --git a/src/test/regress/sql/numeric_big.sql b/src/test/regress/sql/numeric_big.sql index 8f40c200c7..60080e85a5 100644 --- a/src/test/regress/sql/numeric_big.sql +++ b/src/test/regress/sql/numeric_big.sql @@ -645,3 +645,698 @@ SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_power_10_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; + + +-- +-- Test code path for raising to integer powers +-- + +-- base less than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for p in {-20..20} +-- do +-- b="0.084738" +-- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) +-- echo "($b, $p, $r)," +-- done + +WITH t(b, p, bc_result) AS (VALUES +(0.084738, -20, 2744326694304960114888.7859130502035257), +(0.084738, -19, 232548755422013710215.4459407000481464), +(0.084738, -18, 19705716436950597776.2364581230406798), +(0.084738, -17, 1669822999434319754.3627249884302211), +(0.084738, -16, 141497461326065387.3451885900696001), +(0.084738, -15, 11990211877848128.7928565907453178), +(0.084738, -14, 1016026574105094.7376490817865767), +(0.084738, -13, 86096059836517.5178789078924309), +(0.084738, -12, 7295607918426.8214300228969888), +(0.084738, -11, 618215223791.6519943372802450), +(0.084738, -10, 52386321633.6570066961524534), +(0.084738, -9, 4439112122.5928274334185666), +(0.084738, -8, 376161483.0442710110530225), +(0.084738, -7, 31875171.7502054369346110), +(0.084738, -6, 2701038.3037689083149651), +(0.084738, -5, 228880.5837847697527935), +(0.084738, -4, 19394.8829087538193122), +(0.084738, -3, 1643.4835879219811409), +(0.084738, -2, 139.2655122733328379), +(0.084738, -1, 11.8010809790176780), +(0.084738, 0, 1), +(0.084738, 1, .084738), +(0.084738, 2, .007180528644), +(0.084738, 3, .0006084636362353), +(0.084738, 4, .0000515599916073), +(0.084738, 5, .0000043690905688), +(0.084738, 6, .0000003702279966), +(0.084738, 7, .0000000313723800), +(0.084738, 8, .0000000026584327), +(0.084738, 9, .0000000002252703), +(0.084738, 10, .0000000000190890), +(0.084738, 11, .0000000000016176), +(0.084738, 12, .0000000000001371), +(0.084738, 13, .0000000000000116), +(0.084738, 14, .0000000000000010), +(0.084738, 15, .0000000000000001), +(0.084738, 16, .0000000000000000), +(0.084738, 17, .0000000000000000), +(0.084738, 18, .0000000000000000), +(0.084738, 19, .0000000000000000), +(0.084738, 20, .0000000000000000)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + +-- base greater than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for p in {-20..20} +-- do +-- b="37.821637" +-- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) +-- echo "($b, $p, $r)," +-- done + +WITH t(b, p, bc_result) AS (VALUES +(37.821637, -20, .0000000000000000), +(37.821637, -19, .0000000000000000), +(37.821637, -18, .0000000000000000), +(37.821637, -17, .0000000000000000), +(37.821637, -16, .0000000000000000), +(37.821637, -15, .0000000000000000), +(37.821637, -14, .0000000000000000), +(37.821637, -13, .0000000000000000), +(37.821637, -12, .0000000000000000), +(37.821637, -11, .0000000000000000), +(37.821637, -10, .0000000000000002), +(37.821637, -9, .0000000000000063), +(37.821637, -8, .0000000000002388), +(37.821637, -7, .0000000000090327), +(37.821637, -6, .0000000003416316), +(37.821637, -5, .0000000129210673), +(37.821637, -4, .0000004886959182), +(37.821637, -3, .0000184832796213), +(37.821637, -2, .0006990678924066), +(37.821637, -1, .0264398920649574), +(37.821637, 0, 1), +(37.821637, 1, 37.821637), +(37.821637, 2, 1430.476225359769), +(37.821637, 3, 54102.9525326873775219), +(37.821637, 4, 2046262.2313195326271135), +(37.821637, 5, 77392987.3197773940323425), +(37.821637, 6, 2927129472.7542235178972258), +(37.821637, 7, 110708828370.5116321107718772), +(37.821637, 8, 4187189119324.7924539711577286), +(37.821637, 9, 158366346921451.9852944363360812), +(37.821637, 10, 5989674486279224.5007355092228730), +(37.821637, 11, 226539294168214309.7083246628376531), +(37.821637, 12, 8568086950266418559.9938312759931069), +(37.821637, 13, 324059074417413536066.1494087598581043), +(37.821637, 14, 12256444679171401239980.3109258799733927), +(37.821637, 15, 463558801566202198479885.2069857662592280), +(37.821637, 16, 17532552720991931019508170.1002855156233684), +(37.821637, 17, 663109844696719094948877928.0672523682648687), +(37.821637, 18, 25079899837245684700124994552.6717306599041850), +(37.821637, 19, 948562867640665366544581398598.1275771806665398), +(37.821637, 20, 35876200451584291931921101974730.6901038166532866)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + +-- +-- Tests for raising to non-integer powers +-- + +-- base less than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for n in {-20..20} +-- do +-- b="0.06933247" +-- p="$n.342987" +-- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) +-- echo "($b, $p, $r)," +-- done + +WITH t(b, p, bc_result) AS (VALUES +(0.06933247, -20.342987, 379149253615977128356318.39406340), +(0.06933247, -19.342987, 26287354251852125772450.59436685), +(0.06933247, -18.342987, 1822567200045909954554.65766042), +(0.06933247, -17.342987, 126363085720167050546.86216560), +(0.06933247, -16.342987, 8761064849800910427.02880469), +(0.06933247, -15.342987, 607426265866876128.15466179), +(0.06933247, -14.342987, 42114363355427213.14899924), +(0.06933247, -13.342987, 2919892833909256.59283660), +(0.06933247, -12.342987, 202443382310228.51544515), +(0.06933247, -11.342987, 14035899730722.44924025), +(0.06933247, -10.342987, 973143597003.32229028), +(0.06933247, -9.342987, 67470449244.92493259), +(0.06933247, -8.342987, 4677892898.16028054), +(0.06933247, -7.342987, 324329869.02491071), +(0.06933247, -6.342987, 22486590.914273551), +(0.06933247, -5.342987, 1559050.8899661435), +(0.06933247, -4.342987, 108092.84905705095), +(0.06933247, -3.342987, 7494.3442144625131), +(0.06933247, -2.342987, 519.60139541889576), +(0.06933247, -1.342987, 36.025248159838727), +(0.06933247, 0.342987, .40036522320023350), +(0.06933247, 1.342987, .02775830982657349), +(0.06933247, 2.342987, .001924552183301612), +(0.06933247, 3.342987, .0001334339565121935), +(0.06933247, 4.342987, .000009251305786862961), +(0.06933247, 5.342987, .0000006414158809285026), +(0.06933247, 6.342987, .00000004447094732199898), +(0.06933247, 7.342987, .000000003083280621074075), +(0.06933247, 8.342987, .0000000002137714611621997), +(0.06933247, 9.342987, .00000000001482130341788437), +(0.06933247, 10.342987, .000000000001027597574581366), +(0.06933247, 11.342987, .00000000000007124587801173530), +(0.06933247, 12.342987, .000000000000004939652699872298), +(0.06933247, 13.342987, .0000000000000003424783226243151), +(0.06933247, 14.342987, .00000000000000002374486802900065), +(0.06933247, 15.342987, .000000000000000001646290350274646), +(0.06933247, 16.342987, .0000000000000000001141413763217064), +(0.06933247, 17.342987, .000000000000000000007913703549583420), +(0.06933247, 18.342987, .0000000000000000000005486766139403860), +(0.06933247, 19.342987, .00000000000000000000003804110487572339), +(0.06933247, 20.342987, .000000000000000000000002637483762562946)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + +-- base greater than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of POW(): +-- +-- for n in {-20..20} +-- do +-- b="27.234987" +-- p="$n.230957" +-- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) +-- echo "($b, $p, $r)," +-- done + +WITH t(b, p, bc_result) AS (VALUES +(27.234987, -20.230957, .000000000000000000000000000009247064512095633), +(27.234987, -19.230957, .0000000000000000000000000002518436817750859), +(27.234987, -18.230957, .000000000000000000000000006858959399176602), +(27.234987, -17.230957, .0000000000000000000000001868036700701026), +(27.234987, -16.230957, .000000000000000000000005087595525911532), +(27.234987, -15.230957, .0000000000000000000001385605980094587), +(27.234987, -14.230957, .000000000000000000003773696085499835), +(27.234987, -13.230957, .0000000000000000001027765638305389), +(27.234987, -12.230957, .000000000000000002799118379829397), +(27.234987, -11.230957, .00000000000000007623395268611469), +(27.234987, -10.230957, .000000000000002076230710364949), +(27.234987, -9.230957, .00000000000005654611640579014), +(27.234987, -8.230957, .000000000001540032745212181), +(27.234987, -7.230957, .00000000004194277179542807), +(27.234987, -6.230957, .000000001142310844592450), +(27.234987, -5.230957, .00000003111082100243440), +(27.234987, -4.230957, .0000008473028055606278), +(27.234987, -3.230957, .00002307628089450723), +(27.234987, -2.230957, .0006284822101702527), +(27.234987, -1.230957, .01711670482371810), +(27.234987, 0.230957, 2.1451253063142300), +(27.234987, 1.230957, 58.422459830839071), +(27.234987, 2.230957, 1591.1349340009243), +(27.234987, 3.230957, 43334.539242761031), +(27.234987, 4.230957, 1180215.6129275865), +(27.234987, 5.230957, 32143156.875279851), +(27.234987, 6.230957, 875418459.63720737), +(27.234987, 7.230957, 23842010367.779367), +(27.234987, 8.230957, 649336842420.336290), +(27.234987, 9.230957, 17684680461938.907402), +(27.234987, 10.230957, 481642042480060.137900), +(27.234987, 11.230957, 13117514765597885.614921), +(27.234987, 12.230957, 357255344113366461.949871), +(27.234987, 13.230957, 9729844652608062117.440722), +(27.234987, 14.230957, 264992192625800087863.690528), +(27.234987, 15.230957, 7217058921265161257566.469315), +(27.234987, 16.230957, 196556505898890690402726.443417), +(27.234987, 17.230957, 5353213882921711267539279.451015), +(27.234987, 18.230957, 145794710509592328389185797.837767), +(27.234987, 19.230957, 3970717045397510438979206144.696206), +(27.234987, 20.230957, 108142427112079606637962972621.121293)) +SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; + +-- +-- Tests for EXP() +-- + +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of EXP(): +-- +-- for n in {-20..20} +-- do +-- x="$n.29837" +-- r=$(bc -ql <<< "scale=500 ; e($x)" | head -n 1) +-- echo "($x, $r)," +-- done + +WITH t(x, bc_result) AS (VALUES +(-20.29837, .000000001529431101152222), +(-19.29837, .000000004157424770142192), +(-18.29837, .00000001130105220586304), +(-17.29837, .00000003071944485366452), +(-16.29837, .00000008350410872606600), +(-15.29837, .0000002269877013517336), +(-14.29837, .0000006170165438681061), +(-13.29837, .000001677224859055276), +(-12.29837, .000004559169856609741), +(-11.29837, .00001239310857408049), +(-10.29837, .00003368796183504298), +(-9.29837, .00009157337449401917), +(-8.29837, .0002489222398577673), +(-7.29837, .0006766408013046928), +(-6.29837, .001839300394580514), +(-5.29837, .004999736839665763), +(-4.29837, .01359069379834070), +(-3.29837, .03694333598818056), +(-2.29837, .1004223988993283), +(-1.29837, .2729763820983097), +(0.29837, 1.3476603299656679), +(1.29837, 3.6633205858807959), +(2.29837, 9.9579377804197108), +(3.29837, 27.068481317440698), +(4.29837, 73.579760889182206), +(5.29837, 200.01052696742555), +(6.29837, 543.68498095607070), +(7.29837, 1477.8890041389891), +(8.29837, 4017.3188244304487), +(9.29837, 10920.204759575742), +(10.29837, 29684.194161006717), +(11.29837, 80690.005580314652), +(12.29837, 219338.17590722828), +(13.29837, 596222.97785597218), +(14.29837, 1620702.0864156289), +(15.29837, 4405525.0308492653), +(16.29837, 11975458.636179032), +(17.29837, 32552671.598188404), +(18.29837, 88487335.673150406), +(19.29837, 240533516.60908059), +(20.29837, 653837887.33381570)) +SELECT x, bc_result, exp(x), exp(x)-bc_result AS diff FROM t; + +-- +-- Tests for LN() +-- + +-- input very small +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done + +WITH t(x, bc_result) AS (VALUES +('1.0e-1', -2.3025850929940457), +('1.0e-2', -4.6051701859880914), +('1.0e-3', -6.9077552789821371), +('1.0e-4', -9.2103403719761827), +('1.0e-5', -11.512925464970228), +('1.0e-6', -13.815510557964274), +('1.0e-7', -16.118095650958320), +('1.0e-8', -18.420680743952365), +('1.0e-9', -20.723265836946411), +('1.0e-10', -23.025850929940457), +('1.0e-11', -25.328436022934503), +('1.0e-12', -27.631021115928548), +('1.0e-13', -29.933606208922594), +('1.0e-14', -32.236191301916640), +('1.0e-15', -34.5387763949106853), +('1.0e-16', -36.84136148790473094), +('1.0e-17', -39.143946580898776628), +('1.0e-18', -41.4465316738928223123), +('1.0e-19', -43.74911676688686799634), +('1.0e-20', -46.051701859880913680360), +('1.0e-21', -48.3542869528749593643778), +('1.0e-22', -50.65687204586900504839581), +('1.0e-23', -52.959457138863050732413803), +('1.0e-24', -55.2620422318570964164317949), +('1.0e-25', -57.56462732485114210044978637), +('1.0e-26', -59.867212417845187784467777822), +('1.0e-27', -62.1697975108392334684857692765), +('1.0e-28', -64.47238260383327915250376073116), +('1.0e-29', -66.774967696827324836521752185847), +('1.0e-30', -69.0775527898213705205397436405309), +('1.0e-31', -71.38013788281541620455773509521529), +('1.0e-32', -73.682722975809461888575726549899655), +('1.0e-33', -75.9853080688035075725937180045840189), +('1.0e-34', -78.28789316179755325661170945926838306), +('1.0e-35', -80.590478254791598940629700913952747266), +('1.0e-36', -82.8930633477856446246476923686371114736), +('1.0e-37', -85.19564844077969030866568382332147568124), +('1.0e-38', -87.498233533773735992683675278005839888842), +('1.0e-39', -89.8008186267677816767016667326902040964430), +('1.0e-40', -92.10340371976182736071965818737456830404406)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + +-- input very close to but smaller than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1-10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done + +WITH t(x, bc_result) AS (VALUES +('1.0e-1', -.10536051565782630), +('1.0e-2', -.010050335853501441), +('1.0e-3', -.0010005003335835335), +('1.0e-4', -.00010000500033335834), +('1.0e-5', -.000010000050000333336), +('1.0e-6', -.0000010000005000003333), +('1.0e-7', -.00000010000000500000033), +('1.0e-8', -.000000010000000050000000), +('1.0e-9', -.0000000010000000005000000), +('1.0e-10', -.00000000010000000000500000), +('1.0e-11', -.000000000010000000000050000), +('1.0e-12', -.0000000000010000000000005000), +('1.0e-13', -.00000000000010000000000000500), +('1.0e-14', -.000000000000010000000000000050), +('1.0e-15', -.0000000000000010000000000000005), +('1.0e-16', -.00000000000000010000000000000001), +('1.0e-17', -.000000000000000010000000000000000), +('1.0e-18', -.0000000000000000010000000000000000), +('1.0e-19', -.00000000000000000010000000000000000), +('1.0e-20', -.000000000000000000010000000000000000), +('1.0e-21', -.0000000000000000000010000000000000000), +('1.0e-22', -.00000000000000000000010000000000000000), +('1.0e-23', -.000000000000000000000010000000000000000), +('1.0e-24', -.0000000000000000000000010000000000000000), +('1.0e-25', -.00000000000000000000000010000000000000000), +('1.0e-26', -.000000000000000000000000010000000000000000), +('1.0e-27', -.0000000000000000000000000010000000000000000), +('1.0e-28', -.00000000000000000000000000010000000000000000), +('1.0e-29', -.000000000000000000000000000010000000000000000), +('1.0e-30', -.0000000000000000000000000000010000000000000000), +('1.0e-31', -.00000000000000000000000000000010000000000000000), +('1.0e-32', -.000000000000000000000000000000010000000000000000), +('1.0e-33', -.0000000000000000000000000000000010000000000000000), +('1.0e-34', -.00000000000000000000000000000000010000000000000000), +('1.0e-35', -.000000000000000000000000000000000010000000000000000), +('1.0e-36', -.0000000000000000000000000000000000010000000000000000), +('1.0e-37', -.00000000000000000000000000000000000010000000000000000), +('1.0e-38', -.000000000000000000000000000000000000010000000000000000), +('1.0e-39', -.0000000000000000000000000000000000000010000000000000000), +('1.0e-40', -.00000000000000000000000000000000000000010000000000000000)) +SELECT '1-'||x, bc_result, ln(1.0-x::numeric), ln(1.0-x::numeric)-bc_result AS diff FROM t; + +-- input very close to but larger than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1+10^-$p)" | head -n 1) +-- echo "('1.0e-$p', $l)," +-- done + +WITH t(x, bc_result) AS (VALUES +('1.0e-1', .09531017980432486), +('1.0e-2', .009950330853168083), +('1.0e-3', .0009995003330835332), +('1.0e-4', .00009999500033330834), +('1.0e-5', .000009999950000333331), +('1.0e-6', .0000009999995000003333), +('1.0e-7', .00000009999999500000033), +('1.0e-8', .000000009999999950000000), +('1.0e-9', .0000000009999999995000000), +('1.0e-10', .00000000009999999999500000), +('1.0e-11', .000000000009999999999950000), +('1.0e-12', .0000000000009999999999995000), +('1.0e-13', .00000000000009999999999999500), +('1.0e-14', .000000000000009999999999999950), +('1.0e-15', .0000000000000009999999999999995), +('1.0e-16', .00000000000000010000000000000000), +('1.0e-17', .000000000000000010000000000000000), +('1.0e-18', .0000000000000000010000000000000000), +('1.0e-19', .00000000000000000010000000000000000), +('1.0e-20', .000000000000000000010000000000000000), +('1.0e-21', .0000000000000000000010000000000000000), +('1.0e-22', .00000000000000000000010000000000000000), +('1.0e-23', .000000000000000000000010000000000000000), +('1.0e-24', .0000000000000000000000010000000000000000), +('1.0e-25', .00000000000000000000000010000000000000000), +('1.0e-26', .000000000000000000000000010000000000000000), +('1.0e-27', .0000000000000000000000000010000000000000000), +('1.0e-28', .00000000000000000000000000010000000000000000), +('1.0e-29', .000000000000000000000000000010000000000000000), +('1.0e-30', .0000000000000000000000000000010000000000000000), +('1.0e-31', .00000000000000000000000000000010000000000000000), +('1.0e-32', .000000000000000000000000000000010000000000000000), +('1.0e-33', .0000000000000000000000000000000010000000000000000), +('1.0e-34', .00000000000000000000000000000000010000000000000000), +('1.0e-35', .000000000000000000000000000000000010000000000000000), +('1.0e-36', .0000000000000000000000000000000000010000000000000000), +('1.0e-37', .00000000000000000000000000000000000010000000000000000), +('1.0e-38', .000000000000000000000000000000000000010000000000000000), +('1.0e-39', .0000000000000000000000000000000000000010000000000000000), +('1.0e-40', .00000000000000000000000000000000000000010000000000000000)) +SELECT '1+'||x, bc_result, ln(1.0+x::numeric), ln(1.0+x::numeric)-bc_result AS diff FROM t; + +-- input very large +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(10^$p)" | head -n 1) +-- echo "('1.0e$p', $l)," +-- done + +WITH t(x, bc_result) AS (VALUES +('1.0e1', 2.3025850929940457), +('1.0e2', 4.6051701859880914), +('1.0e3', 6.9077552789821371), +('1.0e4', 9.2103403719761827), +('1.0e5', 11.512925464970228), +('1.0e6', 13.815510557964274), +('1.0e7', 16.118095650958320), +('1.0e8', 18.420680743952365), +('1.0e9', 20.723265836946411), +('1.0e10', 23.025850929940457), +('1.0e11', 25.328436022934503), +('1.0e12', 27.631021115928548), +('1.0e13', 29.933606208922594), +('1.0e14', 32.236191301916640), +('1.0e15', 34.538776394910685), +('1.0e16', 36.841361487904731), +('1.0e17', 39.143946580898777), +('1.0e18', 41.446531673892822), +('1.0e19', 43.749116766886868), +('1.0e20', 46.051701859880914), +('1.0e21', 48.354286952874959), +('1.0e22', 50.656872045869005), +('1.0e23', 52.959457138863051), +('1.0e24', 55.262042231857096), +('1.0e25', 57.564627324851142), +('1.0e26', 59.867212417845188), +('1.0e27', 62.169797510839233), +('1.0e28', 64.472382603833279), +('1.0e29', 66.774967696827325), +('1.0e30', 69.077552789821371), +('1.0e31', 71.380137882815416), +('1.0e32', 73.682722975809462), +('1.0e33', 75.985308068803508), +('1.0e34', 78.287893161797553), +('1.0e35', 80.590478254791599), +('1.0e36', 82.893063347785645), +('1.0e37', 85.195648440779690), +('1.0e38', 87.498233533773736), +('1.0e39', 89.800818626767782), +('1.0e40', 92.103403719761827)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + +-- input huge +-- +-- bc(1) results computed with a scale of 1000 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..10} +-- do +-- l=$(bc -ql <<< "scale=1000 ; l(10^${p}00)" | head -n 1) +-- echo "('1.0e${p}00', $l)," +-- done + +WITH t(x, bc_result) AS (VALUES +('1.0e100', 230.25850929940457), +('1.0e200', 460.51701859880914), +('1.0e300', 690.77552789821371), +('1.0e400', 921.03403719761827), +('1.0e500', 1151.2925464970228), +('1.0e600', 1381.5510557964274), +('1.0e700', 1611.8095650958320), +('1.0e800', 1842.0680743952365), +('1.0e900', 2072.3265836946411), +('1.0e1000', 2302.5850929940457)) +SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; + +-- +-- Tests for LOG() (base 10) +-- + +-- input very small, exact result known +WITH t(x) AS (SELECT '1e-'||n FROM generate_series(1, 100) g(n)) +SELECT x, log(x::numeric) FROM t; + +-- input very small, non-exact results +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..50..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l($d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done + +WITH t(x, bc_result) AS (VALUES +('9.0e-1', -.04575749056067513), +('6.0e-1', -.2218487496163564), +('3.0e-1', -.5228787452803376), +('9.0e-8', -7.045757490560675), +('6.0e-8', -7.221848749616356), +('3.0e-8', -7.522878745280338), +('9.0e-15', -14.0457574905606751), +('6.0e-15', -14.2218487496163564), +('3.0e-15', -14.5228787452803376), +('9.0e-22', -21.04575749056067512540994), +('6.0e-22', -21.22184874961635636749123), +('3.0e-22', -21.52287874528033756270497), +('9.0e-29', -28.045757490560675125409944193490), +('6.0e-29', -28.221848749616356367491233202020), +('3.0e-29', -28.522878745280337562704972096745), +('9.0e-36', -35.0457574905606751254099441934897693816), +('6.0e-36', -35.2218487496163563674912332020203916640), +('3.0e-36', -35.5228787452803375627049720967448846908), +('9.0e-43', -42.04575749056067512540994419348976938159974227), +('6.0e-43', -42.22184874961635636749123320202039166403168125), +('3.0e-43', -42.52287874528033756270497209674488469079987114), +('9.0e-50', -49.045757490560675125409944193489769381599742271618608), +('6.0e-50', -49.221848749616356367491233202020391664031681254347196), +('3.0e-50', -49.522878745280337562704972096744884690799871135809304)) +SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t; + +-- input very close to but smaller than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1-$d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done + +WITH t(x, bc_result) AS (VALUES +('9.0e-1', -1.0000000000000000), +('6.0e-1', -.3979400086720376), +('3.0e-1', -.1549019599857432), +('9.0e-8', -.000000039086505130185422), +('6.0e-8', -.000000026057669695925208), +('3.0e-8', -.000000013028834652530076), +('9.0e-15', -.0000000000000039086503371292840), +('6.0e-15', -.0000000000000026057668914195188), +('3.0e-15', -.0000000000000013028834457097574), +('9.0e-22', -.00000000000000000000039086503371292664), +('6.0e-22', -.00000000000000000000026057668914195110), +('3.0e-22', -.00000000000000000000013028834457097555), +('9.0e-29', -.000000000000000000000000000039086503371292664), +('6.0e-29', -.000000000000000000000000000026057668914195110), +('3.0e-29', -.000000000000000000000000000013028834457097555), +('9.0e-36', -.0000000000000000000000000000000000039086503371292664), +('6.0e-36', -.0000000000000000000000000000000000026057668914195110), +('3.0e-36', -.0000000000000000000000000000000000013028834457097555)) +SELECT '1-'||x, bc_result, log(1.0-x::numeric), log(1.0-x::numeric)-bc_result AS diff FROM t; + +-- input very close to but larger than 1 +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {1..40..7} +-- do +-- for d in {9..1..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l(1+$d*10^-$p) / l(10)" | head -n 1) +-- echo "('${d}.0e-$p', $l)," +-- done +-- done + +WITH t(x, bc_result) AS (VALUES +('9.0e-1', .2787536009528290), +('6.0e-1', .2041199826559248), +('3.0e-1', .1139433523068368), +('9.0e-8', .000000039086501612400118), +('6.0e-8', .000000026057668132465074), +('3.0e-8', .000000013028834261665042), +('9.0e-15', .0000000000000039086503371292489), +('6.0e-15', .0000000000000026057668914195031), +('3.0e-15', .0000000000000013028834457097535), +('9.0e-22', .00000000000000000000039086503371292664), +('6.0e-22', .00000000000000000000026057668914195110), +('3.0e-22', .00000000000000000000013028834457097555), +('9.0e-29', .000000000000000000000000000039086503371292664), +('6.0e-29', .000000000000000000000000000026057668914195110), +('3.0e-29', .000000000000000000000000000013028834457097555), +('9.0e-36', .0000000000000000000000000000000000039086503371292664), +('6.0e-36', .0000000000000000000000000000000000026057668914195110), +('3.0e-36', .0000000000000000000000000000000000013028834457097555)) +SELECT '1+'||x, bc_result, log(1.0+x::numeric), log(1.0+x::numeric)-bc_result AS diff FROM t; + +-- input very large, exact result known +WITH t(x) AS (SELECT '1e'||n FROM generate_series(1, 100) g(n)) +SELECT x, log(x::numeric) FROM t; + +-- input very large, non-exact results +-- +-- bc(1) results computed with a scale of 500 and truncated using the script +-- below, and then rounded by hand to match the precision of LN(): +-- +-- for p in {10..50..7} +-- do +-- for d in {2..9..3} +-- do +-- l=$(bc -ql <<< "scale=500 ; l($d*10^$p) / l(10)" | head -n 1) +-- echo "('${d}.0e$p', $l)," +-- done +-- done + +WITH t(x, bc_result) AS (VALUES +('2.0e10', 10.301029995663981), +('5.0e10', 10.698970004336019), +('8.0e10', 10.903089986991944), +('2.0e17', 17.301029995663981), +('5.0e17', 17.698970004336019), +('8.0e17', 17.903089986991944), +('2.0e24', 24.301029995663981), +('5.0e24', 24.698970004336019), +('8.0e24', 24.903089986991944), +('2.0e31', 31.301029995663981), +('5.0e31', 31.698970004336019), +('8.0e31', 31.903089986991944), +('2.0e38', 38.301029995663981), +('5.0e38', 38.698970004336019), +('8.0e38', 38.903089986991944), +('2.0e45', 45.30102999566398), +('5.0e45', 45.69897000433602), +('8.0e45', 45.90308998699194)) +SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t;