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 <[email protected]> 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;
