You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2022/05/05 10:10:19 UTC

[commons-statistics] 01/02: Avoid BigDecimal computing constants on class initialisation

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git

commit f346bc30de4fdd2ed81552d62276fa2e482f404a
Author: aherbert <ah...@apache.org>
AuthorDate: Thu May 5 11:01:08 2022 +0100

    Avoid BigDecimal computing constants on class initialisation
    
    The constants are precomputed and verified in a unit test.
---
 .../statistics/distribution/ExtendedPrecision.java | 20 ++++++------------
 .../distribution/ExtendedPrecisionTest.java        | 24 ++++++++++++++++++++--
 2 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExtendedPrecision.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExtendedPrecision.java
index fd7c401..6ad73a9 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExtendedPrecision.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExtendedPrecision.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.statistics.distribution;
 
-import java.math.BigDecimal;
-
 /**
  * Computes extended precision floating-point operations.
  *
@@ -29,8 +27,12 @@ import java.math.BigDecimal;
  * <p>Adapted from {@code org.apache.commons.numbers.core.ExtendedPrecion}.
  */
 final class ExtendedPrecision {
-    /** sqrt(2 pi). Computed to 64-digits. */
-    private static final String SQRT_TWO_PI = "2.506628274631000502415765284811045253006986740609938316629923576";
+    /** sqrt(2 pi) as a double. Computed to 64-digits precision and converted to double. */
+    static final double SQRT2PI = 2.5066282746310007;
+    /** Round-off from sqrt(2 pi) as a double.
+     * Computed from the value sqrt(2 pi) to 64-digits precision minus {@link #SQRT2PI}. */
+    static final double SQRT2PI_R = -1.8328579980459167e-16;
+
     /**
      * The multiplier used to split the double value into high and low parts. From
      * Dekker (1971): "The constant should be chosen equal to 2^(p - p/2) + 1,
@@ -46,14 +48,10 @@ final class ExtendedPrecision {
     private static final double SCALE_UP = 0x1.0p600;
     /** Scale down by 2^600. */
     private static final double SCALE_DOWN = 0x1.0p-600;
-    /** sqrt(2 pi) as a double. */
-    private static final double SQRT2PI;
     /** Upper bits of sqrt(2 pi). */
     private static final double SQRT2PI_H;
     /** Lower bits of sqrt(2 pi). */
     private static final double SQRT2PI_L;
-    /** Round-off from sqrt(2 pi) as a double. */
-    private static final double SQRT2PI_R;
     /** X-value where {@code exp(-0.5*x*x)} cannot increase accuracy using the round-off
      * from x squared. */
     private static final int EXP_M_HALF_XX_MIN_VALUE = 2;
@@ -63,12 +61,6 @@ final class ExtendedPrecision {
 
     static {
         // Initialise constants
-        final BigDecimal sqrt2pi = new BigDecimal(SQRT_TWO_PI);
-
-        // Use a 106-bit number as:
-        // (SQRT2PI, SQRT2PI_R)
-        SQRT2PI = sqrt2pi.doubleValue();
-        SQRT2PI_R = sqrt2pi.subtract(new BigDecimal(SQRT2PI)).doubleValue();
 
         // Split the upper 53-bits for extended precision multiplication
         SQRT2PI_H = highPartUnscaled(SQRT2PI);
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ExtendedPrecisionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ExtendedPrecisionTest.java
index 6784249..43c392e 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ExtendedPrecisionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ExtendedPrecisionTest.java
@@ -34,8 +34,10 @@ import org.junit.jupiter.params.provider.ValueSource;
 class ExtendedPrecisionTest {
     /** sqrt(2). */
     private static final double ROOT2 = Math.sqrt(2.0);
-    /** sqrt(2 * pi) to 64 digits. This is 1 ULP different from Math.sqrt(2 * Math.PI). */
-    private static final double ROOT2PI = 2.506628274631000502415765284811045253006986740609938316629923576;
+    /** sqrt(2 pi) as a String. Computed to 64-digits. */
+    private static final String SQRT_TWO_PI = "2.506628274631000502415765284811045253006986740609938316629923576";
+    /** sqrt(2 pi) as a double. Note: This is 1 ULP different from Math.sqrt(2 * Math.PI). */
+    private static final double ROOT2PI = Double.parseDouble(SQRT_TWO_PI);
     /** The sum of the squared ULP error for the first standard computation for sqrt(2 * x * x). */
     private static final RMS SQRT2XX_RMS1 = new RMS();
     /** The sum of the squared ULP error for the second standard computation for sqrt(2 * x * x). */
@@ -96,6 +98,24 @@ class ExtendedPrecisionTest {
         }
     }
 
+    @Test
+    void testSqrt2PiConstants() {
+        final BigDecimal sqrt2pi = new BigDecimal(SQRT_TWO_PI);
+
+        // Use a 106-bit number as:
+        // (value, roundOff)
+        final double value = sqrt2pi.doubleValue();
+        final double roundOff = sqrt2pi.subtract(new BigDecimal(value)).doubleValue();
+        // Adding the round-off does not change the value
+        Assertions.assertEquals(ExtendedPrecision.SQRT2PI,
+                                ExtendedPrecision.SQRT2PI + ExtendedPrecision.SQRT2PI_R, "value + round-off");
+        // Check constants
+        Assertions.assertEquals(value, ExtendedPrecision.SQRT2PI, "sqrt(2 pi)");
+        Assertions.assertEquals(roundOff, ExtendedPrecision.SQRT2PI_R, "sqrt(2 pi) round-off");
+        // Sanity check against JDK Math
+        Assertions.assertEquals(value, Math.sqrt(2 * Math.PI), Math.ulp(value), "Math.sqrt(2 pi)");
+    }
+
     @Test
     void testSqrt2xxUnderAndOverflow() {
         final double x = 1.5;