Clarify the choice of rscale in numeric_sqrt().
authorDean Rasheed <[email protected]>
Thu, 2 Feb 2023 09:41:22 +0000 (09:41 +0000)
committerDean Rasheed <[email protected]>
Thu, 2 Feb 2023 09:41:22 +0000 (09:41 +0000)
Improve the comment explaining the choice of rscale in numeric_sqrt(),
and ensure that the code works consistently when other values of
NBASE/DEC_DIGITS are used.

Note that, in practice, we always expect DEC_DIGITS == 4, and this
does not change the computation in that case.

Joel Jacobson and Dean Rasheed

Discussion: https://postgr.es/m/06712c29-98e9-43b3-98da-f234d81c6e49%40app.fastmail.com

src/backend/utils/adt/numeric.c

index 834339dcff2ec51aa70948f81b1eab3d63ba90ca..08c841675d883459680576fbf02a502087b93180 100644 (file)
@@ -3693,8 +3693,21 @@ numeric_sqrt(PG_FUNCTION_ARGS)
 
    init_var(&result);
 
-   /* Assume the input was normalized, so arg.weight is accurate */
-   sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1;
+   /*
+    * Assume the input was normalized, so arg.weight is accurate.  The result
+    * then has at least sweight = floor(arg.weight * DEC_DIGITS / 2 + 1)
+    * digits before the decimal point.  When DEC_DIGITS is even, we can save
+    * a few cycles, since the division is exact and there is no need to round
+    * towards negative infinity.
+    */
+#if DEC_DIGITS == ((DEC_DIGITS / 2) * 2)
+   sweight = arg.weight * DEC_DIGITS / 2 + 1;
+#else
+   if (arg.weight >= 0)
+       sweight = arg.weight * DEC_DIGITS / 2 + 1;
+   else
+       sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;
+#endif
 
    rscale = NUMERIC_MIN_SIG_DIGITS - sweight;
    rscale = Max(rscale, arg.dscale);