@@ -571,8 +571,8 @@ static void log_var(const NumericVar *base, const NumericVar *num,
571571 NumericVar * result );
572572static void power_var (const NumericVar * base , const NumericVar * exp ,
573573 NumericVar * result );
574- static void power_var_int (const NumericVar * base , int exp , NumericVar * result ,
575- int rscale );
574+ static void power_var_int (const NumericVar * base , int exp , int exp_dscale ,
575+ NumericVar * result );
576576static void power_ten_int (int exp , NumericVar * result );
577577
578578static int cmp_abs (const NumericVar * var1 , const NumericVar * var2 );
@@ -10335,13 +10335,8 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
1033510335 {
1033610336 if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX )
1033710337 {
10338- /* Okay, select rscale */
10339- rscale = NUMERIC_MIN_SIG_DIGITS ;
10340- rscale = Max (rscale , base -> dscale );
10341- rscale = Max (rscale , NUMERIC_MIN_DISPLAY_SCALE );
10342- rscale = Min (rscale , NUMERIC_MAX_DISPLAY_SCALE );
10343-
10344- power_var_int (base , (int ) expval64 , result , rscale );
10338+ /* Okay, use power_var_int */
10339+ power_var_int (base , (int ) expval64 , exp -> dscale , result );
1034510340 return ;
1034610341 }
1034710342 }
@@ -10475,19 +10470,76 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
1047510470 * power_var_int() -
1047610471 *
1047710472 * Raise base to the power of exp, where exp is an integer.
10473+ *
10474+ * Note: this routine chooses dscale of the result.
1047810475 */
1047910476static void
10480- power_var_int (const NumericVar * base , int exp , NumericVar * result , int rscale )
10477+ power_var_int (const NumericVar * base , int exp , int exp_dscale ,
10478+ NumericVar * result )
1048110479{
1048210480 double f ;
1048310481 int p ;
1048410482 int i ;
10483+ int rscale ;
1048510484 int sig_digits ;
1048610485 unsigned int mask ;
1048710486 bool neg ;
1048810487 NumericVar base_prod ;
1048910488 int local_rscale ;
1049010489
10490+ /*
10491+ * Choose the result scale. For this we need an estimate of the decimal
10492+ * weight of the result, which we obtain by approximating using double
10493+ * precision arithmetic.
10494+ *
10495+ * We also perform crude overflow/underflow tests here so that we can exit
10496+ * early if the result is sure to overflow/underflow, and to guard against
10497+ * integer overflow when choosing the result scale.
10498+ */
10499+ if (base -> ndigits != 0 )
10500+ {
10501+ /*----------
10502+ * Choose f (double) and p (int) such that base ~= f * 10^p.
10503+ * Then log10(result) = log10(base^exp) ~= exp * (log10(f) + p).
10504+ *----------
10505+ */
10506+ f = base -> digits [0 ];
10507+ p = base -> weight * DEC_DIGITS ;
10508+
10509+ for (i = 1 ; i < base -> ndigits && i * DEC_DIGITS < 16 ; i ++ )
10510+ {
10511+ f = f * NBASE + base -> digits [i ];
10512+ p -= DEC_DIGITS ;
10513+ }
10514+
10515+ f = exp * (log10 (f ) + p ); /* approximate decimal result weight */
10516+ }
10517+ else
10518+ f = 0 ; /* result is 0 or 1 (weight 0), or error */
10519+
10520+ /* overflow/underflow tests with fuzz factors */
10521+ if (f > (SHRT_MAX + 1 ) * DEC_DIGITS )
10522+ ereport (ERROR ,
10523+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
10524+ errmsg ("value overflows numeric format" )));
10525+ if (f + 1 < - NUMERIC_MAX_DISPLAY_SCALE )
10526+ {
10527+ zero_var (result );
10528+ result -> dscale = NUMERIC_MAX_DISPLAY_SCALE ;
10529+ return ;
10530+ }
10531+
10532+ /*
10533+ * Choose the result scale in the same way as power_var(), so it has at
10534+ * least NUMERIC_MIN_SIG_DIGITS significant digits and is not less than
10535+ * either input's display scale.
10536+ */
10537+ rscale = NUMERIC_MIN_SIG_DIGITS - (int ) f ;
10538+ rscale = Max (rscale , base -> dscale );
10539+ rscale = Max (rscale , exp_dscale );
10540+ rscale = Max (rscale , NUMERIC_MIN_DISPLAY_SCALE );
10541+ rscale = Min (rscale , NUMERIC_MAX_DISPLAY_SCALE );
10542+
1049110543 /* Handle some common special cases, as well as corner cases */
1049210544 switch (exp )
1049310545 {
@@ -10532,43 +10584,15 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
1053210584 * The general case repeatedly multiplies base according to the bit
1053310585 * pattern of exp.
1053410586 *
10535- * First we need to estimate the weight of the result so that we know how
10536- * many significant digits are needed.
10587+ * The local rscale used for each multiplication is varied to keep a fixed
10588+ * number of significant digits, sufficient to give the required result
10589+ * scale.
1053710590 */
10538- f = base -> digits [0 ];
10539- p = base -> weight * DEC_DIGITS ;
10540-
10541- for (i = 1 ; i < base -> ndigits && i * DEC_DIGITS < 16 ; i ++ )
10542- {
10543- f = f * NBASE + base -> digits [i ];
10544- p -= DEC_DIGITS ;
10545- }
10546-
10547- /*----------
10548- * We have base ~= f * 10^p
10549- * so log10(result) = log10(base^exp) ~= exp * (log10(f) + p)
10550- *----------
10551- */
10552- f = exp * (log10 (f ) + p );
10553-
10554- /*
10555- * Apply crude overflow/underflow tests so we can exit early if the result
10556- * certainly will overflow/underflow.
10557- */
10558- if (f > 3 * SHRT_MAX * DEC_DIGITS )
10559- ereport (ERROR ,
10560- (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
10561- errmsg ("value overflows numeric format" )));
10562- if (f + 1 < - rscale || f + 1 < - NUMERIC_MAX_DISPLAY_SCALE )
10563- {
10564- zero_var (result );
10565- result -> dscale = rscale ;
10566- return ;
10567- }
1056810591
1056910592 /*
1057010593 * Approximate number of significant digits in the result. Note that the
10571- * underflow test above means that this is necessarily >= 0.
10594+ * underflow test above, together with the choice of rscale, ensures that
10595+ * this approximation is necessarily > 0.
1057210596 */
1057310597 sig_digits = 1 + rscale + (int ) f ;
1057410598
0 commit comments