@@ -571,8 +571,8 @@ static void log_var(const NumericVar *base, const NumericVar *num,
571
571
NumericVar * result );
572
572
static void power_var (const NumericVar * base , const NumericVar * exp ,
573
573
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 );
576
576
static void power_ten_int (int exp , NumericVar * result );
577
577
578
578
static int cmp_abs (const NumericVar * var1 , const NumericVar * var2 );
@@ -10335,13 +10335,8 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
10335
10335
{
10336
10336
if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX )
10337
10337
{
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 );
10345
10340
return ;
10346
10341
}
10347
10342
}
@@ -10475,19 +10470,76 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
10475
10470
* power_var_int() -
10476
10471
*
10477
10472
* Raise base to the power of exp, where exp is an integer.
10473
+ *
10474
+ * Note: this routine chooses dscale of the result.
10478
10475
*/
10479
10476
static 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 )
10481
10479
{
10482
10480
double f ;
10483
10481
int p ;
10484
10482
int i ;
10483
+ int rscale ;
10485
10484
int sig_digits ;
10486
10485
unsigned int mask ;
10487
10486
bool neg ;
10488
10487
NumericVar base_prod ;
10489
10488
int local_rscale ;
10490
10489
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
+
10491
10543
/* Handle some common special cases, as well as corner cases */
10492
10544
switch (exp )
10493
10545
{
@@ -10532,43 +10584,15 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
10532
10584
* The general case repeatedly multiplies base according to the bit
10533
10585
* pattern of exp.
10534
10586
*
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.
10537
10590
*/
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
- }
10568
10591
10569
10592
/*
10570
10593
* 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.
10572
10596
*/
10573
10597
sig_digits = 1 + rscale + (int ) f ;
10574
10598
0 commit comments