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;