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 2023/08/25 09:32:12 UTC

[commons-numbers] branch master updated: NUMBERS-193: Add a double-double (DD) number

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-numbers.git


The following commit(s) were added to refs/heads/master by this push:
     new fab24443 NUMBERS-193: Add a double-double (DD) number
fab24443 is described below

commit fab24443938b17b71eedc19fd3aa0267e98f7dcf
Author: aherbert <ah...@apache.org>
AuthorDate: Fri Jun 2 14:34:48 2023 +0100

    NUMBERS-193: Add a double-double (DD) number
    
    Add a double-double extended precision floating-point number.
    
    This adapts the DD class from Commons Statistics Inference as a general
    use number.
---
 .github/workflows/maven.yml                        |    6 +-
 .../java/org/apache/commons/numbers/core/DD.java   | 2212 +++++++++++++++
 .../org/apache/commons/numbers/core/DDMath.java    |  480 ++++
 .../org/apache/commons/numbers/core/DDExt.java     |  735 +++++
 .../org/apache/commons/numbers/core/DDTest.java    | 2980 ++++++++++++++++++++
 .../org/apache/commons/numbers/core/TestUtils.java |  303 ++
 .../org/apache/commons/numbers/core/sqrt-512.csv   |  537 ++++
 .../org/apache/commons/numbers/core/sqrt0.csv      |  537 ++++
 .../org/apache/commons/numbers/core/sqrt512.csv    |  537 ++++
 commons-numbers-examples/examples-jmh/pom.xml      |   25 +-
 .../numbers/examples/jmh/core/DDPerformance.java   |  889 ++++++
 .../numbers/examples/jmh/core/DoublePrecision.java |    4 +-
 .../jmh/core/KolmogorovSmirnovDistribution.java    |  706 +++++
 .../commons/numbers/examples/jmh/core/SDD.java     | 1728 ++++++++++++
 .../examples/jmh/core/DDPerformanceTest.java       |   54 +
 .../checkstyle/checkstyle-suppressions.xml         |    4 +
 src/main/resources/pmd/pmd-ruleset.xml             |   21 +-
 .../resources/spotbugs/spotbugs-exclude-filter.xml |    5 +
 18 files changed, 11751 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index b460a8a8..aec11423 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -46,8 +46,8 @@ jobs:
       # Examples require Java 11+.
       # Building javadoc errors when run with the package phase with an error about
       # the module path and the unamed module (despite using an automatic module name).
-      # Here we run the build and javadoc generation separately.
+      # Here we run the build and javadoc generation separately (which requires install of sources)
       if: matrix.java > 8
       run: |
-        mvn -V --no-transfer-progress -P commons-numbers-examples -Dmaven.javadoc.skip
-        mvn -P commons-numbers-examples javadoc:javadoc
+        mvn -V --no-transfer-progress -P commons-numbers-examples clean install -Dmaven.javadoc.skip
+        mvn -P commons-numbers-examples javadoc:javadoc -Dmaven.javadoc.skip=false
diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DD.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DD.java
new file mode 100644
index 00000000..38923d67
--- /dev/null
+++ b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DD.java
@@ -0,0 +1,2212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.core;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * Computes double-double floating-point operations.
+ *
+ * <p>A double-double is an unevaluated sum of two IEEE double precision numbers capable of
+ * representing at least 106 bits of significand. A normalized double-double number {@code (x, xx)}
+ *  satisfies the condition that the parts are non-overlapping in magnitude such that:
+ * <pre>
+ * |x| &gt; |xx|
+ * x == x + xx
+ * </pre>
+ *
+ * <p>This implementation assumes a normalized representation during operations on a {@code DD}
+ * number and computes results as a normalized representation. Any double-double number
+ * can be normalized by summation of the parts (see {@link #ofSum(double, double) ofSum}).
+ * Note that the number {@code (x, xx)} may also be referred to using the labels high and low
+ * to indicate the magnitude of the parts as
+ * {@code (x}<sub>hi</sub>{@code , x}<sub>lo</sub>{@code )}, or using a numerical suffix for the
+ * parts as {@code (x}<sub>0</sub>{@code , x}<sub>1</sub>{@code )}. The numerical suffix is
+ * typically used when the number has an arbitrary number of parts.
+ *
+ * <p>The double-double class is immutable.
+ *
+ * <p><b>Construction</b>
+ *
+ * <p>Factory methods to create a {@code DD} that are exact use the prefix {@code of}. Methods
+ * that create the closest possible representation use the prefix {@code from}. These methods
+ * may suffer a possible loss of precision during conversion.
+ *
+ * <p>Primitive values of type {@code double}, {@code int} and {@code long} are
+ * converted exactly to a {@code DD}.
+ *
+ * <p>The {@code DD} class can also be created as the result of an arithmetic operation on a pair
+ * of {@code double} operands. The resulting {@code DD} has the IEEE754 {@code double} result
+ * of the operation in the first part, and the second part contains the round-off lost from the
+ * operation due to rounding. Construction using add ({@code +}), subtract ({@code -}) and
+ * multiply ({@code *}) operators are exact. Construction using division ({@code /}) may be
+ * inexact if the quotient is not representable.
+ *
+ * <p>Note that it is more efficient to create a {@code DD} from a {@code double} operation than
+ * to create two {@code DD} values and combine them with the same operation. The result will be
+ * the same for add, subtract and multiply but may lose precision for divide.
+ * <pre>{@code
+ * // Inefficient
+ * DD a = DD.of(1.23).add(DD.of(4.56));
+ * // Optimal
+ * DD b = DD.ofSum(1.23, 4.56);
+ *
+ * // Inefficient and may lose precision
+ * DD c = DD.of(1.23).divide(DD.of(4.56));
+ * // Optimal
+ * DD d = DD.fromQuotient(1.23, 4.56);
+ * }</pre>
+ *
+ * <p>It is not possible to directly specify the two parts of the number.
+ * The two parts must be added using {@link #ofSum(double, double) ofSum}.
+ * If the two parts already represent a number such {@code (x, xx)} such that {@code x == x + xx}
+ * then the magnitudes of the parts will be unchanged; any signed zeros may be subject to a sign
+ * change.
+ *
+ * <p><b>Primitive operands</b>
+ *
+ * <p>Operations are provided using a {@code DD} operand or a {@code double} operand.
+ * Implicit type conversion allows methods with a {@code double} operand to be used
+ * with other primitives such as {@code int} or {@code long}. Note that casting of a {@code long}
+ * to a {@code double} may result in loss of precision.
+ * To maintain the full precision of a {@code long} first convert the value to a {@code DD} using
+ * {@link #of(long)} and use the same arithmetic operation using the {@code DD} operand.
+ *
+ * <p><b>Accuracy</b>
+ *
+ * <p>Add and multiply operations using two {@code double} values operands are computed to an
+ * exact {@code DD} result (see {@link #ofSum(double, double) ofSum} and
+ * {@link #ofProduct(double, double) ofProduct}). Operations involving a {@code DD} and another
+ * operand, either {@code double} or {@code DD}, are not exact.
+ *
+ * <p>This class is not intended to perform exact arithmetic. Arbitrary precision arithmetic is
+ * available using {@link BigDecimal}. Single operations will compute the {@code DD} result within
+ * a tolerance of the 106-bit exact result. This far exceeds the accuracy of {@code double}
+ * arithmetic. The reduced accuracy is a compromise to deliver increased performance.
+ * The class is intended to reduce error in equivalent {@code double} arithmetic operations where
+ * the {@code double} valued result is required to high accuracy. Although it
+ * is possible to reduce error to 2<sup>-106</sup> for all operations, the additional computation
+ * would impact performance and would require multiple chained operations to potentially
+ * observe a different result when the final {@code DD} is converted to a {@code double}.
+ *
+ * <p><b>Canonical representation</b>
+ *
+ * <p>The double-double number is the sum of its parts. The canonical representation of the
+ * number is the explicit value of the parts. The {@link #toString()} method is provided to
+ * convert to a String representation of the parts formatted as a tuple.
+ *
+ * <p>The class implements {@link #equals(Object)} and {@link #hashCode()} and allows usage as
+ * a key in a Set or Map. Equality requires <em>binary</em> equivalence of the parts. Note that
+ * representations of zero using different combinations of +/- 0.0 are not considered equal.
+ * Also note that many non-normalized double-double numbers can represent the same number.
+ * Double-double numbers can be normalized before operations that involve {@link #equals(Object)}
+ * by {@link #ofSum(double, double) adding} the parts; this is exact for a finite sum
+ * and provides equality support for non-zero numbers. Alternatively exact numerical equality
+ * and comparisons are supported by conversion to a {@link #bigDecimalValue() BigDecimal}
+ * representation. Note that {@link BigDecimal} does not support non-finite values.
+ *
+ * <p><b>Overflow, underflow and non-finite support</b>
+ *
+ * <p>A double-double number is limited to the same finite range as a {@code double}
+ * ({@value Double#MIN_VALUE} to {@value Double#MAX_VALUE}). This class is intended for use when
+ * the ultimate result is finite and intermediate values do not approach infinity or zero.
+ *
+ * <p>This implementation does not support IEEE standards for handling infinite and NaN when used
+ * in arithmetic operations. Computations may split a 64-bit double into two parts and/or use
+ * subtraction of intermediate terms to compute round-off parts. These operations may generate
+ * infinite values due to overflow which then propagate through further operations to NaN,
+ * for example computing the round-off using {@code Inf - Inf = NaN}.
+ *
+ * <p>Operations that involve splitting a double (multiply, divide) are safe
+ * when the base 2 exponent is below 996. This puts an upper limit of approximately +/-6.7e299 on
+ * any values to be split; in practice the arguments to multiply and divide operations are further
+ * constrained by the expected finite value of the product or quotient.
+ *
+ * <p>Likewise the smallest value that can be represented is {@link Double#MIN_VALUE}. The full
+ * 106-bit accuracy will be lost when intermediates are within 2<sup>53</sup> of
+ * {@link Double#MIN_NORMAL}.
+ *
+ * <p>The {@code DD} result can be verified by checking it is a {@link #isFinite() finite}
+ * evaluated sum. Computations expecting to approach over or underflow must use scaling of
+ * intermediate terms (see {@link #frexp(int[]) frexp} and {@link #scalb(int) scalb}) and
+ * appropriate management of the current base 2 scale.
+ *
+ * <p>References:
+ * <ol>
+ * <li>
+ * Dekker, T.J. (1971)
+ * <a href="https://doi.org/10.1007/BF01397083">
+ * A floating-point technique for extending the available precision</a>
+ * Numerische Mathematik, 18:224–242.
+ * <li>
+ * Shewchuk, J.R. (1997)
+ * <a href="https://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
+ * Arbitrary Precision Floating-Point Arithmetic</a>.
+ * <li>
+ * Hide, Y, Li, X.S. and Bailey, D.H. (2008)
+ * <a href="https://www.davidhbailey.com/dhbpapers/qd.pdf">
+ * Library for Double-Double and Quad-Double Arithmetic</a>.
+ * </ol>
+ *
+ * @since 1.2
+ */
+public final class DD
+    extends Number
+    implements NativeOperators<DD>,
+               Serializable {
+    // Caveat:
+    //
+    // The code below uses many additions/subtractions that may
+    // appear redundant. However, they should NOT be simplified, as they
+    // do use IEEE754 floating point arithmetic rounding properties.
+    //
+    // Algorithms are based on computing the product or sum of two values x and y in
+    // extended precision. The standard result is stored using a double (high part z) and
+    // the round-off error (or low part zz) is stored in a second double, e.g:
+    // x * y = (z, zz); z + zz = x * y
+    // x + y = (z, zz); z + zz = x + y
+    //
+    // The building blocks for double-double arithmetic are:
+    //
+    // Fast-Two-Sum: Addition of two doubles (ordered |x| > |y|) to a double-double
+    // Two-Sum: Addition of two doubles (unordered) to a double-double
+    // Two-Prod: Multiplication of two doubles to a double-double
+    //
+    // These are used to create functions operating on double and double-double numbers.
+    //
+    // To sum multiple (z, zz) results ideally the parts are sorted in order of
+    // non-decreasing magnitude and summed. This is exact if each number's most significant
+    // bit is below the least significant bit of the next (i.e. does not
+    // overlap). Creating non-overlapping parts requires a rebalancing
+    // of adjacent pairs using a summation z + zz = (z1, zz1) iteratively through the parts
+    // (see Shewchuk (1997) Grow-Expansion and Expansion-Sum [2]).
+    //
+    // Accurate summation of an expansion (more than one double value) to a double-double
+    // performs a two-sum through the expansion e (length m).
+    // The single pass with two-sum ensures that the final term e_m is a good approximation
+    // for e: |e - e_m| < ulp(e_m); and the sum of the parts to
+    // e_(m-1) is within 1 ULP of the round-off ulp(|e - e_m|).
+    // These final two terms create the double-double result using two-sum.
+    //
+    // Maintenance of 1 ULP precision in the round-off component for all double-double
+    // operations is a performance burden. This class avoids this requirement to provide
+    // a compromise between accuracy and performance.
+
+    /**
+     * A double-double number representing one.
+     */
+    public static final DD ONE = new DD(1, 0);
+    /**
+     * A double-double number representing zero.
+     */
+    public static final DD ZERO = new DD(0, 0);
+
+    /**
+     * 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,
+     * where p is the number of binary digits in the mantissa". Here p is 53
+     * and the multiplier is {@code 2^27 + 1}.
+     */
+    private static final double MULTIPLIER = 1.0 + 0x1.0p27;
+    /** The mask to extract the raw 11-bit exponent.
+     * The value must be shifted 52-bits to remove the mantissa bits. */
+    private static final int EXP_MASK = 0x7ff;
+    /** The value 2046 converted for use if using {@link Integer#compareUnsigned(int, int)}.
+     * This requires adding {@link Integer#MIN_VALUE} to 2046. */
+    private static final int CMP_UNSIGNED_2046 = Integer.MIN_VALUE + 2046;
+    /** The value -1 converted for use if using {@link Integer#compareUnsigned(int, int)}.
+     * This requires adding {@link Integer#MIN_VALUE} to -1. */
+    private static final int CMP_UNSIGNED_MINUS_1 = Integer.MIN_VALUE - 1;
+    /** The value 1022 converted for use if using {@link Integer#compareUnsigned(int, int)}.
+     * This requires adding {@link Integer#MIN_VALUE} to 1022. */
+    private static final int CMP_UNSIGNED_1022 = Integer.MIN_VALUE + 1022;
+    /** 2^512. */
+    private static final double TWO_POW_512 = 0x1.0p512;
+    /** 2^-512. */
+    private static final double TWO_POW_M512 = 0x1.0p-512;
+    /** 2^53. Any double with a magnitude above this is an even integer. */
+    private static final double TWO_POW_53 = 0x1.0p53;
+    /** Mask to extract the high 32-bits from a long. */
+    private static final long HIGH32_MASK = 0xffff_ffff_0000_0000L;
+    /** Mask to remove the sign bit from a long. */
+    private static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+    /** Mask to extract the 52-bit mantissa from a long representation of a double. */
+    private static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+    /** Exponent offset in IEEE754 representation. */
+    private static final int EXPONENT_OFFSET = 1023;
+    /** 0.5. */
+    private static final double HALF = 0.5;
+    /** The limit for safe multiplication of {@code x*y}, assuming values above 1.
+     * Used to maintain positive values during the power computation. */
+    private static final double SAFE_MULTIPLY = 0x1.0p500;
+
+    /**
+     * The size of the buffer for {@link #toString()}.
+     *
+     * <p>The longest double will require a sign, a maximum of 17 digits, the decimal place
+     * and the exponent, e.g. for max value this is 24 chars: -1.7976931348623157e+308.
+     * Set the buffer size to twice this and round up to a power of 2 thus
+     * allowing for formatting characters. The size is 64.
+     */
+    private static final int TO_STRING_SIZE = 64;
+    /** {@link #toString() String representation}. */
+    private static final char FORMAT_START = '(';
+    /** {@link #toString() String representation}. */
+    private static final char FORMAT_END = ')';
+    /** {@link #toString() String representation}. */
+    private static final char FORMAT_SEP = ',';
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20230701L;
+
+    /** The high part of the double-double number. */
+    private final double x;
+    /** The low part of the double-double number. */
+    private final double xx;
+
+    /**
+     * Create a double-double number {@code (x, xx)}.
+     *
+     * @param x High part.
+     * @param xx Low part.
+     */
+    private DD(double x, double xx) {
+        this.x = x;
+        this.xx = xx;
+    }
+
+    // Conversion constructors
+
+    /**
+     * Creates the double-double number as the value {@code (x, 0)}.
+     *
+     * @param x Value.
+     * @return the double-double
+     */
+    public static DD of(double x) {
+        return new DD(x, 0);
+    }
+
+    /**
+     * Creates the double-double number as the value {@code (x, xx)}.
+     *
+     * <p><strong>Warning</strong>
+     *
+     * <p>The arguments are used directly. No checks are made that they represent
+     * a normalized double-double number: {@code x == x + xx}.
+     *
+     * <p>This method is exposed for testing.
+     *
+     * @param x High part.
+     * @param xx Low part.
+     * @return the double-double
+     * @see #twoSum(double, double)
+     */
+    static DD of(double x, double xx) {
+        return new DD(x, xx);
+    }
+
+    /**
+     * Creates the double-double number as the value {@code (x, 0)}.
+     *
+     * <p>Note this method exists to avoid using {@link #of(long)} for {@code integer}
+     * arguments; the {@code long} variation is slower as it preserves all 64-bits
+     * of information.
+     *
+     * @param x Value.
+     * @return the double-double
+     * @see #of(long)
+     */
+    public static DD of(int x) {
+        return new DD(x, 0);
+    }
+
+    /**
+     * Creates the double-double number with the high part equal to {@code (double) x}
+     * and the low part equal to any remaining bits.
+     *
+     * <p>Note this method preserves all 64-bits of precision. Faster construction can be
+     * achieved using up to 53-bits of precision using {@code of((double) x)}.
+     *
+     * @param x Value.
+     * @return the double-double
+     * @see #of(double)
+     */
+    public static DD of(long x) {
+        // Note: Casting the long to a double can lose bits due to rounding.
+        // These are not recoverable using lo = x - (long)((double) x)
+        // if the double is rounded outside the range of a long (i.e. 2^53).
+        // Split the long into two 32-bit numbers that are exactly representable
+        // and add them.
+        final long a = x & HIGH32_MASK;
+        final long b = x - a;
+        // When x is positive: a > b or a == 0
+        // When x is negative: |a| > |b|
+        return fastTwoSum(a, b);
+    }
+
+    /**
+     * Creates the double-double number {@code (z, zz)} using the {@code double} representation
+     * of the argument {@code x}; the low part is the {@code double} representation of the
+     * round-off error.
+     * <pre>
+     * double z = x.doubleValue();
+     * double zz = x.subtract(new BigDecimal(z)).doubleValue();
+     * </pre>
+     * <p>If the value cannot be represented as a finite value the result will have an
+     * infinite high part and the low part is undefined.
+     *
+     * <p>Note: This conversion can lose information about the precision of the BigDecimal value.
+     * The result is the closest double-double representation to the value.
+     *
+     * @param x Value.
+     * @return the double-double
+     */
+    public static DD from(BigDecimal x) {
+        final double z = x.doubleValue();
+        // Guard against an infinite throwing a exception
+        final double zz = Double.isInfinite(z) ? 0 : x.subtract(new BigDecimal(z)).doubleValue();
+        // No normalisation here
+        return new DD(z, zz);
+    }
+
+    // Arithmetic constructors:
+
+    /**
+     * Returns a {@code DD} whose value is {@code (x + y)}.
+     * The values are not required to be ordered by magnitude,
+     * i.e. the result is commutative: {@code x + y == y + x}.
+     *
+     * <p>This method ignores special handling of non-normal numbers and
+     * overflow within the extended precision computation.
+     * This creates the following special cases:
+     *
+     * <ul>
+     *  <li>If {@code x + y} is infinite then the low part is NaN.
+     *  <li>If {@code x} or {@code y} is infinite or NaN then the low part is NaN.
+     *  <li>If {@code x + y} is sub-normal or zero then the low part is +/-0.0.
+     * </ul>
+     *
+     * <p>An invalid result can be identified using {@link #isFinite()}.
+     *
+     * <p>The result is the exact double-double representation of the sum.
+     *
+     * @param x Addend.
+     * @param y Addend.
+     * @return the sum {@code x + y}.
+     * @see #ofDifference(double, double)
+     */
+    public static DD ofSum(double x, double y) {
+        return twoSum(x, y);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (x - y)}.
+     * The values are not required to be ordered by magnitude,
+     * i.e. the result matches a negation and addition: {@code x - y == -y + x}.
+     *
+     * <p>Computes the same results as {@link #ofSum(double, double) ofSum(a, -b)}.
+     * See that method for details of special cases.
+     *
+     * <p>An invalid result can be identified using {@link #isFinite()}.
+     *
+     * <p>The result is the exact double-double representation of the difference.
+     *
+     * @param x Minuend.
+     * @param y Subtrahend.
+     * @return {@code x - y}.
+     * @see #ofSum(double, double)
+     */
+    public static DD ofDifference(double x, double y) {
+        return twoDiff(x, y);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (x * y)}.
+     *
+     * <p>This method ignores special handling of non-normal numbers and intermediate
+     * overflow within the extended precision computation.
+     * This creates the following special cases:
+     *
+     * <ul>
+     *  <li>If either {@code |x|} or {@code |y|} multiplied by {@code 1 + 2^27}
+     *      is infinite (intermediate overflow) then the low part is NaN.
+     *  <li>If {@code x * y} is infinite then the low part is NaN.
+     *  <li>If {@code x} or {@code y} is infinite or NaN then the low part is NaN.
+     *  <li>If {@code x * y} is sub-normal or zero then the low part is +/-0.0.
+     * </ul>
+     *
+     * <p>An invalid result can be identified using {@link #isFinite()}.
+     *
+     * <p>Note: Ignoring special cases is a design choice for performance. The
+     * method is therefore not a drop-in replacement for
+     * {@code roundOff = Math.fma(x, y, -x * y)}.
+     *
+     * <p>The result is the exact double-double representation of the product.
+     *
+     * @param x Factor.
+     * @param y Factor.
+     * @return the product {@code x * y}.
+     */
+    public static DD ofProduct(double x, double y) {
+        return twoProd(x, y);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (x * x)}.
+     *
+     * <p>This method is an optimisation of {@link #ofProduct(double, double) multiply(x, x)}.
+     * See that method for details of special cases.
+     *
+     * <p>An invalid result can be identified using {@link #isFinite()}.
+     *
+     * <p>The result is the exact double-double representation of the square.
+     *
+     * @param x Factor.
+     * @return the square {@code x * x}.
+     * @see #ofProduct(double, double)
+     */
+    public static DD ofSquare(double x) {
+        return twoSquare(x);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (x / y)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>This method ignores special handling of non-normal numbers and intermediate
+     * overflow within the extended precision computation.
+     * This creates the following special cases:
+     *
+     * <ul>
+     *  <li>If either {@code |x / y|} or {@code |y|} multiplied by {@code 1 + 2^27}
+     *      is infinite (intermediate overflow) then the low part is NaN.
+     *  <li>If {@code x / y} is infinite then the low part is NaN.
+     *  <li>If {@code x} or {@code y} is infinite or NaN then the low part is NaN.
+     *  <li>If {@code x / y} is sub-normal or zero, excluding the previous cases,
+     *      then the low part is +/-0.0.
+     * </ul>
+     *
+     * <p>An invalid result can be identified using {@link #isFinite()}.
+     *
+     * <p>The result is the closest double-double representation to the quotient.
+     *
+     * @param x Dividend.
+     * @param y Divisor.
+     * @return the quotient {@code x / y}.
+     */
+    public static DD fromQuotient(double x, double y) {
+        // Long division
+        // quotient q0 = x / y
+        final double q0 = x / y;
+        // remainder r = x - q0 * y
+        final double p0 = q0 * y;
+        final double p1 = twoProductLow(q0, y, p0);
+        final double r0 = x - p0;
+        final double r1 = twoDiffLow(x, p0, r0) - p1;
+        // correction term q1 = r0 / y
+        final double q1 = (r0 + r1) / y;
+        return new DD(q0, q1);
+    }
+
+    // Properties
+
+    /**
+     * Gets the first part {@code x} of the double-double number {@code (x, xx)}.
+     * In a normalized double-double number this part will have the greatest magnitude.
+     *
+     * <p>This is equivalent to returning the high-part {@code x}<sub>hi</sub> for the number
+     * {@code (x}<sub>hi</sub>{@code , x}<sub>lo</sub>{@code )}.
+     *
+     * @return the first part
+     */
+    public double hi() {
+        return x;
+    }
+
+    /**
+     * Gets the second part {@code xx} of the double-double number {@code (x, xx)}.
+     * In a normalized double-double number this part will have the smallest magnitude.
+     *
+     * <p>This is equivalent to returning the low part {@code x}<sub>lo</sub> for the number
+     * {@code (x}<sub>hi</sub>{@code , x}<sub>lo</sub>{@code )}.
+     *
+     * @return the second part
+     */
+    public double lo() {
+        return xx;
+    }
+
+    /**
+     * Returns {@code true} if the evaluated sum of the parts is finite.
+     *
+     * <p>This method is provided as a utility to check the result of a {@code DD} computation.
+     * Note that for performance the {@code DD} class does not follow IEEE754 arithmetic
+     * for infinite and NaN, and does not protect from overflow of intermediate values in
+     * multiply and divide operations. If this method returns {@code false} following
+     * {@code DD} arithmetic then the computation is not supported to extended precision.
+     *
+     * <p>Note: Any number that returns {@code true} may be converted to the exact
+     * {@link BigDecimal} value.
+     *
+     * @return {@code true} if this instance represents a finite {@code double} value.
+     * @see Double#isFinite(double)
+     * @see #bigDecimalValue()
+     */
+    public boolean isFinite() {
+        return Double.isFinite(x + xx);
+    }
+
+    // Number conversions
+
+    /**
+     * Get the value as a {@code double}. This is the evaluated sum of the parts.
+     *
+     * <p>Note that even when the return value is finite, this conversion can lose
+     * information about the precision of the {@code DD} value.
+     *
+     * <p>Conversion of a finite {@code DD} can also be performed using the
+     * {@link #bigDecimalValue() BigDecimal} representation.
+     *
+     * @return the value converted to a {@code double}
+     * @see #bigDecimalValue()
+     */
+    @Override
+    public double doubleValue() {
+        return x + xx;
+    }
+
+    /**
+     * Get the value as a {@code float}. This is the narrowing primitive conversion of the
+     * {@link #doubleValue()}. This conversion can lose range, resulting in a
+     * {@code float} zero from a nonzero {@code double} and a {@code float} infinity from
+     * a finite {@code double}. A {@code double} NaN is converted to a {@code float} NaN
+     * and a {@code double} infinity is converted to the same-signed {@code float}
+     * infinity.
+     *
+     * <p>Note that even when the return value is finite, this conversion can lose
+     * information about the precision of the {@code DD} value.
+     *
+     * <p>Conversion of a finite {@code DD} can also be performed using the
+     * {@link #bigDecimalValue() BigDecimal} representation.
+     *
+     * @return the value converted to a {@code float}
+     * @see #bigDecimalValue()
+     */
+    @Override
+    public float floatValue() {
+        return (float) doubleValue();
+    }
+
+    /**
+     * Get the value as an {@code int}. This conversion discards the fractional part of the
+     * number and effectively rounds the value to the closest whole number in the direction
+     * of zero. This is the equivalent of a cast of a floating-point number to an integer, for
+     * example {@code (int) -2.75 => -2}.
+     *
+     * <p>Note that this conversion can lose information about the precision of the
+     * {@code DD} value.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If the {@code DD} value is infinite the result is {@link Integer#MAX_VALUE}.
+     *  <li>If the {@code DD} value is -infinite the result is {@link Integer#MIN_VALUE}.
+     *  <li>If the {@code DD} value is NaN the result is 0.
+     * </ul>
+     *
+     * <p>Conversion of a finite {@code DD} can also be performed using the
+     * {@link #bigDecimalValue() BigDecimal} representation. Note that {@link BigDecimal}
+     * conversion rounds to the {@link java.math.BigInteger BigInteger} whole number
+     * representation and returns the low-order 32-bits. Numbers too large for an {@code int}
+     * may change sign. This method ensures the sign is correct by directly rounding to
+     * an {@code int} and returning the respective upper or lower limit for numbers too
+     * large for an {@code int}.
+     *
+     * @return the value converted to an {@code int}
+     * @see #bigDecimalValue()
+     */
+    @Override
+    public int intValue() {
+        // Clip the long value
+        return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, longValue()));
+    }
+
+    /**
+     * Get the value as a {@code long}. This conversion discards the fractional part of the
+     * number and effectively rounds the value to the closest whole number in the direction
+     * of zero. This is the equivalent of a cast of a floating-point number to an integer, for
+     * example {@code (long) -2.75 => -2}.
+     *
+     * <p>Note that this conversion can lose information about the precision of the
+     * {@code DD} value.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If the {@code DD} value is infinite the result is {@link Long#MAX_VALUE}.
+     *  <li>If the {@code DD} value is -infinite the result is {@link Long#MIN_VALUE}.
+     *  <li>If the {@code DD} value is NaN the result is 0.
+     * </ul>
+     *
+     * <p>Conversion of a finite {@code DD} can also be performed using the
+     * {@link #bigDecimalValue() BigDecimal} representation. Note that {@link BigDecimal}
+     * conversion rounds to the {@link java.math.BigInteger BigInteger} whole number
+     * representation and returns the low-order 64-bits. Numbers too large for a {@code long}
+     * may change sign. This method ensures the sign is correct by directly rounding to
+     * a {@code long} and returning the respective upper or lower limit for numbers too
+     * large for a {@code long}.
+     *
+     * @return the value converted to an {@code int}
+     * @see #bigDecimalValue()
+     */
+    @Override
+    public long longValue() {
+        // Assume |hi| > |lo|, i.e. the low part is the round-off
+        final long a = (long) x;
+        // The cast will truncate the value to the range [Long.MIN_VALUE, Long.MAX_VALUE].
+        // If the long converted back to a double is the same value then the high part
+        // was a representable integer and we must use the low part.
+        // Note: The floating-point comparison is intentional.
+        if (a == x) {
+            // Edge case: Any double value above 2^53 is even. To workaround representation
+            // of 2^63 as Long.MAX_VALUE (which is 2^63-1) we can split a into two parts.
+            long a1;
+            long a2;
+            if (Math.abs(x) > TWO_POW_53) {
+                a1 = (long) (x * 0.5);
+                a2 = a1;
+            } else {
+                a1 = a;
+                a2 = 0;
+            }
+
+            // To truncate the fractional part of the double-double towards zero we
+            // convert the low part to a whole number. This must be rounded towards zero
+            // with respect to the sign of the high part.
+            final long b = (long) (a < 0 ? Math.ceil(xx) : Math.floor(xx));
+
+            final long sum = a1 + b + a2;
+            // Avoid overflow. If the sum has changed sign then an overflow occurred.
+            // This happens when high == 2^63 and the low part is additional magnitude.
+            // The xor operation creates a negative if the signs are different.
+            if ((sum ^ a) >= 0) {
+                return sum;
+            }
+        }
+        // Here the high part had a fractional part, was non-finite or was 2^63.
+        // Ignore the low part.
+        return a;
+    }
+
+    /**
+     * Get the value as a {@code BigDecimal}. This is the evaluated sum of the parts;
+     * the conversion is exact.
+     *
+     * <p>The conversion will raise a {@link NumberFormatException} if the number
+     * is non-finite.
+     *
+     * @return the double-double as a {@code BigDecimal}.
+     * @throws NumberFormatException if any part of the number is {@code infinite} or {@code NaN}
+     * @see BigDecimal
+     */
+    public BigDecimal bigDecimalValue() {
+        return new BigDecimal(x).add(new BigDecimal(xx));
+    }
+
+    // Static extended precision methods for computing the round-off component
+    // for double addition and multiplication
+
+    /**
+     * Compute the sum of two numbers {@code a} and {@code b} using
+     * Dekker's two-sum algorithm. The values are required to be ordered by magnitude:
+     * {@code |a| >= |b|}.
+     *
+     * <p>If {@code a} is zero and {@code b} is non-zero the returned value is {@code (b, 0)}.
+     *
+     * @param a First part of sum.
+     * @param b Second part of sum.
+     * @return the sum
+     * @see #fastTwoDiff(double, double)
+     * @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
+     * Shewchuk (1997) Theorum 6</a>
+     */
+    static DD fastTwoSum(double a, double b) {
+        final double x = a + b;
+        return new DD(x, fastTwoSumLow(a, b, x));
+    }
+
+    /**
+     * Compute the round-off of the sum of two numbers {@code a} and {@code b} using
+     * Dekker's two-sum algorithm. The values are required to be ordered by magnitude:
+     * {@code |a| >= |b|}.
+     *
+     * <p>If {@code a} is zero and {@code b} is non-zero the returned value is zero.
+     *
+     * @param a First part of sum.
+     * @param b Second part of sum.
+     * @param x Sum.
+     * @return the sum round-off
+     * @see #fastTwoSum(double, double)
+     */
+    static double fastTwoSumLow(double a, double b, double x) {
+        // (x, xx) = a + b
+        // bVirtual = x - a
+        // xx = b - bVirtual
+        return b - (x - a);
+    }
+
+    /**
+     * Compute the difference of two numbers {@code a} and {@code b} using
+     * Dekker's two-sum algorithm. The values are required to be ordered by magnitude:
+     * {@code |a| >= |b|}.
+     *
+     * <p>Computes the same results as {@link #fastTwoSum(double, double) fastTwoSum(a, -b)}.
+     *
+     * @param a Minuend.
+     * @param b Subtrahend.
+     * @return the difference
+     * @see #fastTwoSum(double, double)
+     * @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
+     * Shewchuk (1997) Theorum 6</a>
+     */
+    static DD fastTwoDiff(double a, double b) {
+        final double x = a - b;
+        return new DD(x, fastTwoDiffLow(a, b, x));
+    }
+
+    /**
+     * Compute the round-off of the difference of two numbers {@code a} and {@code b} using
+     * Dekker's two-sum algorithm. The values are required to be ordered by magnitude:
+     * {@code |a| >= |b|}.
+     *
+     * @param a Minuend.
+     * @param b Subtrahend.
+     * @param x Difference.
+     * @return the difference round-off
+     * @see #fastTwoDiff(double, double)
+     */
+    private static double fastTwoDiffLow(double a, double b, double x) {
+        // (x, xx) = a - b
+        // bVirtual = a - x
+        // xx = bVirtual - b
+        return (a - x) - b;
+    }
+
+    /**
+     * Compute the sum of two numbers {@code a} and {@code b} using
+     * Knuth's two-sum algorithm. The values are not required to be ordered by magnitude,
+     * i.e. the result is commutative {@code s = a + b == b + a}.
+     *
+     * @param a First part of sum.
+     * @param b Second part of sum.
+     * @return the sum
+     * @see #twoDiff(double, double)
+     * @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
+     * Shewchuk (1997) Theorum 7</a>
+     */
+    static DD twoSum(double a, double b) {
+        final double x = a + b;
+        return new DD(x, twoSumLow(a, b, x));
+    }
+
+    /**
+     * Compute the round-off of the sum of two numbers {@code a} and {@code b} using
+     * Knuth two-sum algorithm. The values are not required to be ordered by magnitude,
+     * i.e. the result is commutative {@code s = a + b == b + a}.
+     *
+     * @param a First part of sum.
+     * @param b Second part of sum.
+     * @param x Sum.
+     * @return the sum round-off
+     * @see #twoSum(double, double)
+     */
+    static double twoSumLow(double a, double b, double x) {
+        // (x, xx) = a + b
+        // bVirtual = x - a
+        // aVirtual = x - bVirtual
+        // bRoundoff = b - bVirtual
+        // aRoundoff = a - aVirtual
+        // xx = aRoundoff + bRoundoff
+        final double bVirtual = x - a;
+        return (a - (x - bVirtual)) + (b - bVirtual);
+    }
+
+    /**
+     * Compute the difference of two numbers {@code a} and {@code b} using
+     * Knuth's two-sum algorithm. The values are not required to be ordered by magnitude.
+     *
+     * <p>Computes the same results as {@link #twoSum(double, double) twoSum(a, -b)}.
+     *
+     * @param a Minuend.
+     * @param b Subtrahend.
+     * @return the difference
+     * @see #twoSum(double, double)
+     */
+    static DD twoDiff(double a, double b) {
+        final double x = a - b;
+        return new DD(x, twoDiffLow(a, b, x));
+    }
+
+    /**
+     * Compute the round-off of the difference of two numbers {@code a} and {@code b} using
+     * Knuth two-sum algorithm. The values are not required to be ordered by magnitude,
+     *
+     * @param a Minuend.
+     * @param b Subtrahend.
+     * @param x Difference.
+     * @return the difference round-off
+     * @see #twoDiff(double, double)
+     */
+    private static double twoDiffLow(double a, double b, double x) {
+        // (x, xx) = a - b
+        // bVirtual = a - x
+        // aVirtual = x + bVirtual
+        // bRoundoff = b - bVirtual
+        // aRoundoff = a - aVirtual
+        // xx = aRoundoff - bRoundoff
+        final double bVirtual = a - x;
+        return (a - (x + bVirtual)) - (b - bVirtual);
+    }
+
+    /**
+     * Compute the double-double number {@code (z,zz)} for the exact
+     * product of {@code x} and {@code y}.
+     *
+     * <p>The high part of the number is equal to the product {@code z = x * y}.
+     * The low part is set to the round-off of the {@code double} product.
+     *
+     * <p>This method ignores special handling of non-normal numbers and intermediate
+     * overflow within the extended precision computation.
+     * This creates the following special cases:
+     *
+     * <ul>
+     *  <li>If {@code x * y} is sub-normal or zero then the low part is +/-0.0.
+     *  <li>If {@code x * y} is infinite then the low part is NaN.
+     *  <li>If {@code x} or {@code y} is infinite or NaN then the low part is NaN.
+     *  <li>If either {@code |x|} or {@code |y|} multiplied by {@code 1 + 2^27}
+     *      is infinite (intermediate overflow) then the low part is NaN.
+     * </ul>
+     *
+     * <p>Note: Ignoring special cases is a design choice for performance. The
+     * method is therefore not a drop-in replacement for
+     * {@code round_off = Math.fma(x, y, -x * y)}.
+     *
+     * @param x First factor.
+     * @param y Second factor.
+     * @return the product
+     */
+    static DD twoProd(double x, double y) {
+        final double xy = x * y;
+        // No checks for non-normal xy, or overflow during the split of the arguments
+        return new DD(xy, twoProductLow(x, y, xy));
+    }
+
+    /**
+     * Compute the low part of the double length number {@code (z,zz)} for the exact
+     * product of {@code x} and {@code y} using Dekker's mult12 algorithm. The standard
+     * precision product {@code x*y} must be provided. The numbers {@code x} and {@code y}
+     * are split into high and low parts using Dekker's algorithm.
+     *
+     * <p>Warning: This method does not perform scaling in Dekker's split and large
+     * finite numbers can create NaN results.
+     *
+     * @param x First factor.
+     * @param y Second factor.
+     * @param xy Product of the factors (x * y).
+     * @return the low part of the product double length number
+     * @see #highPart(double)
+     */
+    private static double twoProductLow(double x, double y, double xy) {
+        // Split the numbers using Dekker's algorithm without scaling
+        final double hx = highPart(x);
+        final double lx = x - hx;
+        final double hy = highPart(y);
+        final double ly = y - hy;
+        return twoProductLow(hx, lx, hy, ly, xy);
+    }
+
+    /**
+     * Compute the low part of the double length number {@code (z,zz)} for the exact
+     * product of {@code x} and {@code y} using Dekker's mult12 algorithm. The standard
+     * precision product {@code x*y}, and the high and low parts of the factors must be
+     * provided.
+     *
+     * @param hx High-part of first factor.
+     * @param lx Low-part of first factor.
+     * @param hy High-part of second factor.
+     * @param ly Low-part of second factor.
+     * @param xy Product of the factors (x * y).
+     * @return the low part of the product double length number
+     */
+    static double twoProductLow(double hx, double lx, double hy, double ly, double xy) {
+        // Compute the multiply low part:
+        // err1 = xy - hx * hy
+        // err2 = err1 - lx * hy
+        // err3 = err2 - hx * ly
+        // low = lx * ly - err3
+        return lx * ly - (((xy - hx * hy) - lx * hy) - hx * ly);
+    }
+
+    /**
+     * Compute the double-double number {@code (z,zz)} for the exact
+     * square of {@code x}.
+     *
+     * <p>The high part of the number is equal to the square {@code z = x * x}.
+     * The low part is set to the round-off of the {@code double} square.
+     *
+     * <p>This method is an optimisation of {@link #twoProd(double, double) twoProd(x, x)}.
+     * See that method for details of special cases.
+     *
+     * @param x Factor.
+     * @return the square
+     * @see #twoProd(double, double)
+     */
+    static DD twoSquare(double x) {
+        final double xx = x * x;
+        // No checks for non-normal xy, or overflow during the split of the arguments
+        return new DD(xx, twoSquareLow(x, xx));
+    }
+
+    /**
+     * Compute the low part of the double length number {@code (z,zz)} for the exact
+     * square of {@code x} using Dekker's mult12 algorithm. The standard
+     * precision square {@code x*x} must be provided. The number {@code x}
+     * is split into high and low parts using Dekker's algorithm.
+     *
+     * <p>Warning: This method does not perform scaling in Dekker's split and large
+     * finite numbers can create NaN results.
+     *
+     * @param x Factor.
+     * @param x2 Square of the factor (x * x).
+     * @return the low part of the square double length number
+     * @see #highPart(double)
+     * @see #twoProductLow(double, double, double)
+     */
+    private static double twoSquareLow(double x, double x2) {
+        // See productLowUnscaled
+        final double hx = highPart(x);
+        final double lx = x - hx;
+        return twoSquareLow(hx, lx, x2);
+    }
+
+    /**
+     * Compute the low part of the double length number {@code (z,zz)} for the exact
+     * square of {@code x} using Dekker's mult12 algorithm. The standard
+     * precision square {@code x*x}, and the high and low parts of the factors must be
+     * provided.
+     *
+     * @param hx High-part of factor.
+     * @param lx Low-part of factor.
+     * @param x2 Square of the factor (x * x).
+     * @return the low part of the square double length number
+     */
+    static double twoSquareLow(double hx, double lx, double x2) {
+        return lx * lx - ((x2 - hx * hx) - 2 * lx * hx);
+    }
+
+    /**
+     * Implement Dekker's method to split a value into two parts. Multiplying by (2^s + 1) creates
+     * a big value from which to derive the two split parts.
+     * <pre>
+     * c = (2^s + 1) * a
+     * a_big = c - a
+     * a_hi = c - a_big
+     * a_lo = a - a_hi
+     * a = a_hi + a_lo
+     * </pre>
+     *
+     * <p>The multiplicand allows a p-bit value to be split into
+     * (p-s)-bit value {@code a_hi} and a non-overlapping (s-1)-bit value {@code a_lo}.
+     * Combined they have (p-1) bits of significand but the sign bit of {@code a_lo}
+     * contains a bit of information. The constant is chosen so that s is ceil(p/2) where
+     * the precision p for a double is 53-bits (1-bit of the mantissa is assumed to be
+     * 1 for a non sub-normal number) and s is 27.
+     *
+     * <p>This conversion does not use scaling and the result of overflow is NaN. Overflow
+     * may occur when the exponent of the input value is above 996.
+     *
+     * <p>Splitting a NaN or infinite value will return NaN.
+     *
+     * @param value Value.
+     * @return the high part of the value.
+     * @see Math#getExponent(double)
+     */
+    static double highPart(double value) {
+        final double c = MULTIPLIER * value;
+        return c - (c - value);
+    }
+
+    // Public API operations
+
+    /**
+     * Returns a {@code DD} whose value is the negation of both parts of double-double number.
+     *
+     * @return the negation
+     */
+    @Override
+    public DD negate() {
+        return new DD(-x, -xx);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is the absolute value of the number {@code (x, xx)}
+     * This method assumes that the low part {@code xx} is the smaller magnitude.
+     *
+     * <p>Cases:
+     * <ul>
+     *  <li>If the {@code x} value is negative the result is {@code (-x, -xx)}.
+     *  <li>If the {@code x} value is +/- 0.0 the result is {@code (0.0, 0.0)}; this
+     *      will remove sign information from the round-off component assumed to be zero.
+     *  <li>Otherwise the result is {@code this}.
+     * </ul>
+     *
+     * @return the absolute value
+     * @see #negate()
+     * @see #ZERO
+     */
+    public DD abs() {
+        // Assume |hi| > |lo|, i.e. the low part is the round-off
+        if (x < 0) {
+            return negate();
+        }
+        // NaN, positive or zero
+        // return a canonical absolute of zero
+        return x == 0 ? ZERO : this;
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this + y)}.
+     *
+     * <p>This computes the same result as
+     * {@link #add(DD) add(DD.of(y))}.
+     *
+     * <p>The computed result is within 2 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Value to be added to this number.
+     * @return {@code this + y}.
+     * @see #add(DD)
+     */
+    public DD add(double y) {
+        // (s0, s1) = x + y
+        final double s0 = x + y;
+        final double s1 = twoSumLow(x, y, s0);
+        // Note: if x + y cancel to a non-zero result then s.x is >= 1 ulp of x.
+        // This is larger than xx so fast-two-sum can be used.
+        return fastTwoSum(s0, s1 + xx);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this + y)}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Value to be added to this number.
+     * @return {@code this + y}.
+     */
+    @Override
+    public DD add(DD y) {
+        return add(x, xx, y.x, y.xx);
+    }
+
+    /**
+     * Compute the sum of {@code (x, xx)} and {@code (y, yy)}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the sum
+     * @see #accurateAdd(double, double, double, double)
+     */
+    static DD add(double x, double xx, double y, double yy) {
+        // Sum parts and save
+        // (s0, s1) = x + y
+        final double s0 = x + y;
+        final double s1 = twoSumLow(x, y, s0);
+        // (t0, t1) = xx + yy
+        final double t0 = xx + yy;
+        final double t1 = twoSumLow(xx, yy, t0);
+        // result = s + t
+        // |s1| is >= 1 ulp of max(|x|, |y|)
+        // |t0| is >= 1 ulp of max(|xx|, |yy|)
+        final DD zz = fastTwoSum(s0, s1 + t0);
+        return fastTwoSum(zz.x, zz.xx + t1);
+    }
+
+    /**
+     * Compute the sum of {@code (x, xx)} and {@code y}.
+     *
+     * <p>This computes the same result as
+     * {@link #accurateAdd(double, double, double, double) accurateAdd(x, xx, y, 0)}.
+     *
+     * <p>Note: This is an internal helper method used when accuracy is required.
+     * The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     * The performance is approximately 1.5-fold slower than {@link #add(double)}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y y.
+     * @return the sum
+     */
+    static DD accurateAdd(double x, double xx, double y) {
+        // Grow expansion (Schewchuk): (x, xx) + y -> (s0, s1, s2)
+        DD s = twoSum(xx, y);
+        double s2 = s.xx;
+        s = twoSum(x, s.x);
+        final double s0 = s.x;
+        final double s1 = s.xx;
+        // Compress (Schewchuk Fig. 15): (s0, s1, s2) -> (s0, s1)
+        s = fastTwoSum(s1, s2);
+        s2 = s.xx;
+        s = fastTwoSum(s0, s.x);
+        // Here (s0, s1) = s
+        // e = exact 159-bit result
+        // |e - s0| <= ulp(s0)
+        // |s1 + s2| <= ulp(e - s0)
+        return fastTwoSum(s.x, s2 + s.xx);
+    }
+
+    /**
+     * Compute the sum of {@code (x, xx)} and {@code (y, yy)}.
+     *
+     * <p>The high-part of the result is within 1 ulp of the true sum {@code e}.
+     * The low-part of the result is within 1 ulp of the result of the high-part
+     * subtracted from the true sum {@code e - hi}.
+     *
+     * <p>Note: This is an internal helper method used when accuracy is required.
+     * The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     * The performance is approximately 2-fold slower than {@link #add(DD)}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the sum
+     */
+    static DD accurateAdd(double x, double xx, double y, double yy) {
+        // Expansion sum (Schewchuk Fig 7): (x, xx) + (x, yy) -> (s0, s1, s2, s3)
+        DD s = twoSum(xx, yy);
+        double s3 = s.xx;
+        s = twoSum(x, s.x);
+        // (s0, s1, s2) == (s.x, s.xx, s3)
+        double s0 = s.x;
+        s = twoSum(s.xx, y);
+        double s2 = s.xx;
+        s = twoSum(s0, s.x);
+        // s1 = s.xx
+        s0 = s.x;
+        // Compress (Schewchuk Fig. 15) (s0, s1, s2, s3) -> (s0, s1)
+        s = fastTwoSum(s.xx, s2);
+        final double s1 = s.x;
+        s = fastTwoSum(s.xx, s3);
+        // s2 = s.x
+        s3 = s.xx;
+        s = fastTwoSum(s1, s.x);
+        s2 = s.xx;
+        s = fastTwoSum(s0, s.x);
+        // Here (s0, s1) = s
+        // e = exact 212-bit result
+        // |e - s0| <= ulp(s0)
+        // |s1 + s2 + s3| <= ulp(e - s0)   (Sum magnitudes small to high)
+        return fastTwoSum(s.x, s3 + s2 + s.xx);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this - y)}.
+     *
+     * <p>This computes the same result as {@link #add(DD) add(-y)}.
+     *
+     * <p>The computed result is within 2 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Value to be subtracted from this number.
+     * @return {@code this - y}.
+     * @see #subtract(DD)
+     */
+    public DD subtract(double y) {
+        return add(-y);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this - y)}.
+     *
+     * <p>This computes the same result as {@link #add(DD) add(y.negate())}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Value to be subtracted from this number.
+     * @return {@code this - y}.
+     */
+    @Override
+    public DD subtract(DD y) {
+        return add(x, xx, -y.x, -y.xx);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code this * y}.
+     *
+     * <p>This computes the same result as
+     * {@link #multiply(DD) multiply(DD.of(y))}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Factor.
+     * @return {@code this * y}.
+     * @see #multiply(DD)
+     */
+    public DD multiply(double y) {
+        return multiply(x, xx, y);
+    }
+
+    /**
+     * Compute the multiplication product of {@code (x, xx)} and {@code y}.
+     *
+     * <p>This computes the same result as
+     * {@link #multiply(double, double, double, double) multiply(x, xx, y, 0)}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @return the product
+     * @see #multiply(double, double, double, double)
+     */
+    private static DD multiply(double x, double xx, double y) {
+        // Dekker mul2 with yy=0
+        // (Alternative: Scale expansion (Schewchuk Fig 13))
+        final double hi = x * y;
+        final double lo = twoProductLow(x, y, hi);
+        // Save 2 FLOPS compared to multiply(x, xx, y, 0).
+        // This is reused in divide to save more FLOPS so worth the optimisation.
+        return fastTwoSum(hi, lo + xx * y);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code this * y}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Factor.
+     * @return {@code this * y}.
+     */
+    @Override
+    public DD multiply(DD y) {
+        return multiply(x, xx, y.x, y.xx);
+    }
+
+    /**
+     * Compute the multiplication product of {@code (x, xx)} and {@code (y, yy)}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the product
+     */
+    private static DD multiply(double x, double xx, double y, double yy) {
+        // Dekker mul2
+        // (Alternative: Scale expansion (Schewchuk Fig 13))
+        final double hi = x * y;
+        final double lo = twoProductLow(x, y, hi);
+        return fastTwoSum(hi, lo + (x * yy + xx * y));
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code this * this}.
+     *
+     * <p>This method is an optimisation of {@link #multiply(DD) multiply(this)}.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @return {@code this}<sup>2</sup>
+     * @see #multiply(DD)
+     */
+    public DD square() {
+        return square(x, xx);
+    }
+
+    /**
+     * Compute the square of {@code (x, xx)}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @return the square
+     */
+    private static DD square(double x, double xx) {
+        // Dekker mul2
+        final double hi = x * x;
+        final double lo = twoSquareLow(x, hi);
+        return fastTwoSum(hi, lo + (2 * x * xx));
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this / y)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Divisor.
+     * @return {@code this / y}.
+     */
+    public DD divide(double y) {
+        return divide(x, xx, y);
+    }
+
+    /**
+     * Compute the division of {@code (x, xx)} by {@code y}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @return the quotient
+     */
+    private static DD divide(double x, double xx, double y) {
+        // Long division
+        // quotient q0 = x / y
+        final double q0 = x / y;
+        // remainder r0 = x - q0 * y
+        DD p = twoProd(y, q0);
+        // High accuracy add required
+        DD r = accurateAdd(x, xx, -p.x, -p.xx);
+        // next quotient q1 = r0 / y
+        final double q1 = r.x / y;
+        // remainder r1 = r0 - q1 * y
+        p = twoProd(y, q1);
+        // accurateAdd not used as we do not need r1.xx
+        r = add(r.x, r.xx, -p.x, -p.xx);
+        // next quotient q2 = r1 / y
+        final double q2 = r.x / y;
+        // Collect (q0, q1, q2)
+        final DD q = fastTwoSum(q0, q1);
+        return twoSum(q.x, q.xx + q2);
+    }
+
+    /**
+     * Returns a {@code DD} whose value is {@code (this / y)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y Divisor.
+     * @return {@code this / y}.
+     */
+    @Override
+    public DD divide(DD y) {
+        return divide(x, xx, y.x, y.xx);
+    }
+
+    /**
+     * Compute the division of {@code (x, xx)} by {@code (y, yy)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the quotient
+     */
+    private static DD divide(double x, double xx, double y, double yy) {
+        // Long division
+        // quotient q0 = x / y
+        final double q0 = x / y;
+        // remainder r0 = x - q0 * y
+        DD p = multiply(y, yy, q0);
+        // High accuracy add required
+        DD r = accurateAdd(x, xx, -p.x, -p.xx);
+        // next quotient q1 = r0 / y
+        final double q1 = r.x / y;
+        // remainder r1 = r0 - q1 * y
+        p = multiply(y, yy, q1);
+        // accurateAdd not used as we do not need r1.xx
+        r = add(r.x, r.xx, -p.x, -p.xx);
+        // next quotient q2 = r1 / y
+        final double q2 = r.x / y;
+        // Collect (q0, q1, q2)
+        final DD q = fastTwoSum(q0, q1);
+        return twoSum(q.x, q.xx + q2);
+    }
+
+    /**
+     * Compute the reciprocal of {@code this}.
+     * If {@code this} value is zero the result is undefined.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @return {@code this}<sup>-1</sup>
+     */
+    @Override
+    public DD reciprocal() {
+        return reciprocal(x, xx);
+    }
+
+    /**
+     * Compute the inverse of {@code (y, yy)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the inverse
+     */
+    private static DD reciprocal(double y, double yy) {
+        // As per divide using (x, xx) = (1, 0)
+        // quotient q0 = x / y
+        final double q0 = 1 / y;
+        // remainder r0 = x - q0 * y
+        DD p = multiply(y, yy, q0);
+        // High accuracy add required
+        // This add saves 2 twoSum and 3 fastTwoSum (24 FLOPS) by ignoring the zero low part
+        DD r = accurateAdd(-p.x, -p.xx, 1);
+        // next quotient q1 = r0 / y
+        final double q1 = r.x / y;
+        // remainder r1 = r0 - q1 * y
+        p = multiply(y, yy, q1);
+        // accurateAdd not used as we do not need r1.xx
+        r = add(r.x, r.xx, -p.x, -p.xx);
+        // next quotient q2 = r1 / y
+        final double q2 = r.x / y;
+        // Collect (q0, q1, q2)
+        final DD q = fastTwoSum(q0, q1);
+        return twoSum(q.x, q.xx + q2);
+    }
+
+    /**
+     * Compute the square root of {@code this} number {@code (x, xx)}.
+     *
+     * <p>Uses the result {@code Math.sqrt(x)}
+     * if that result is not a finite normalized {@code double}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code x} is NaN or less than zero, then the result is {@code (NaN, 0)}.
+     *  <li>If {@code x} is positive infinity, then the result is {@code (+infinity, 0)}.
+     *  <li>If {@code x} is positive zero or negative zero, then the result is {@code (x, 0)}.
+     * </ul>
+     *
+     * <p>The computed result is within 4 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * @return {@code sqrt(this)}
+     * @see Math#sqrt(double)
+     * @see Double#MIN_NORMAL
+     */
+    public DD sqrt() {
+        // Standard sqrt
+        final double c = Math.sqrt(x);
+
+        // Here we support {negative, +infinity, nan and zero} edge cases.
+        // This is required to avoid a divide by zero in the following
+        // computation, otherwise (0, 0).sqrt() = (NaN, NaN).
+        if (isNotNormal(c)) {
+            return new DD(c, 0);
+        }
+
+        // Here hi is positive, non-zero and finite; assume lo is also finite
+
+        // Dekker's double precision sqrt2 algorithm.
+        // See Dekker, 1971, pp 242.
+        final double hc = highPart(c);
+        final double lc = c - hc;
+        final double u = c * c;
+        final double uu = twoSquareLow(hc, lc, u);
+        final double cc = (x - u - uu + xx) * 0.5 / c;
+
+        // Extended precision result:
+        // y = c + cc
+        // yy = c - y + cc
+        return fastTwoSum(c, cc);
+    }
+
+    /**
+     * Checks if the number is not normal. This is functionally equivalent to:
+     * <pre>{@code
+     * final double abs = Math.abs(a);
+     * return (abs <= Double.MIN_NORMAL || !(abs <= Double.MAX_VALUE));
+     * }</pre>
+     *
+     * @param a The value.
+     * @return true if the value is not normal
+     */
+    static boolean isNotNormal(double a) {
+        // Sub-normal numbers have a biased exponent of 0.
+        // Inf/NaN numbers have a biased exponent of 2047.
+        // Catch both cases by extracting the raw exponent, subtracting 1
+        // and compare unsigned (so 0 underflows to a unsigned large value).
+        final int baisedExponent = ((int) (Double.doubleToRawLongBits(a) >>> 52)) & EXP_MASK;
+        // Pre-compute the additions used by Integer.compareUnsigned
+        return baisedExponent + CMP_UNSIGNED_MINUS_1 >= CMP_UNSIGNED_2046;
+    }
+
+    /**
+     * Multiply {@code this} number {@code (x, xx)} by an integral power of two.
+     * <pre>
+     * (y, yy) = (x, xx) * 2^exp
+     * </pre>
+     *
+     * <p>The result is rounded as if performed by a single correctly rounded floating-point
+     * multiply. This performs the same result as:
+     * <pre>
+     * y = Math.scalb(x, exp);
+     * yy = Math.scalb(xx, exp);
+     * </pre>
+     *
+     * <p>The implementation computes using a single multiplication if {@code exp}
+     * is in {@code [-1022, 1023]}. Otherwise the parts {@code (x, xx)} are scaled by
+     * repeated multiplication by power-of-two factors. The result is exact unless the scaling
+     * generates sub-normal parts; in this case precision may be lost by a single rounding.
+     *
+     * @param exp Power of two scale factor.
+     * @return the result
+     * @see Math#scalb(double, int)
+     * @see #frexp(int[])
+     */
+    public DD scalb(int exp) {
+        // Handle scaling when 2^n can be represented with a single normal number
+        // n >= -1022 && n <= 1023
+        // Using unsigned compare => n + 1022 <= 1023 + 1022
+        if (exp + CMP_UNSIGNED_1022 < CMP_UNSIGNED_2046) {
+            final double s = twoPow(exp);
+            return new DD(x * s, xx * s);
+        }
+
+        // Scale by multiples of 2^512 (largest representable power of 2).
+        // Scaling requires max 5 multiplications to under/overflow any normal value.
+        // Break this down into e.g.: 2^512^(exp / 512) * 2^(exp % 512)
+        // Number of multiples n = exp / 512   : exp >>> 9
+        // Remainder           m = exp % 512   : exp & 511  (exp must be positive)
+        int n;
+        int m;
+        double p;
+        if (exp < 0) {
+            // Downscaling
+            // (Note: Using an unsigned shift handles negation of min value: -2^31)
+            n = -exp >>> 9;
+            // m = exp % 512
+            m = -(-exp & 511);
+            p = TWO_POW_M512;
+        } else {
+            // Upscaling
+            n = exp >>> 9;
+            m = exp & 511;
+            p = TWO_POW_512;
+        }
+
+        // Multiply by the remainder scaling factor first. The remaining multiplications
+        // are either 2^512 or 2^-512.
+        // Down-scaling to sub-normal will use the final multiplication into a sub-normal result.
+        // Note here that n >= 1 as the n in [-1022, 1023] case has been handled.
+
+        double z0;
+        double z1;
+
+        // Handle n : 1, 2, 3, 4, 5
+        if (n >= 5) {
+            // n >= 5 will be over/underflow. Use an extreme scale factor.
+            // Do not use +/- infinity as this creates NaN if x = 0.
+            // p -> 2^1023 or 2^-1025
+            p *= p * 0.5;
+            z0 = x * p * p * p;
+            z1 = xx * p * p * p;
+            return new DD(z0, z1);
+        }
+
+        final double s = twoPow(m);
+        if (n == 4) {
+            z0 = x * s * p * p * p * p;
+            z1 = xx * s * p * p * p * p;
+        } else if (n == 3) {
+            z0 = x * s * p * p * p;
+            z1 = xx * s * p * p * p;
+        } else if (n == 2) {
+            z0 = x * s * p * p;
+            z1 = xx * s * p * p;
+        } else {
+            // n = 1. Occurs only if exp = -1023.
+            z0 = x * s * p;
+            z1 = xx * s * p;
+        }
+        return new DD(z0, z1);
+    }
+
+    /**
+     * Create a normalized double with the value {@code 2^n}.
+     *
+     * <p>Warning: Do not call with {@code n = -1023}. This will create zero.
+     *
+     * @param n Exponent (in the range [-1022, 1023]).
+     * @return the double
+     */
+    static double twoPow(int n) {
+        return Double.longBitsToDouble(((long) (n + 1023)) << 52);
+    }
+
+    /**
+     * Convert {@code this} number {@code x} to fractional {@code f} and integral
+     * {@code 2^exp} components.
+     * <pre>
+     * x = f * 2^exp
+     * </pre>
+     *
+     * <p>The combined fractional part (f, ff) is in the range {@code [0.5, 1)}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code x} is zero, then the normalized fraction is zero and the exponent is zero.
+     *  <li>If {@code x} is NaN, then the normalized fraction is NaN and the exponent is unspecified.
+     *  <li>If {@code x} is infinite, then the normalized fraction is infinite and the exponent is unspecified.
+     *  <li>If high-part {@code x} is an exact power of 2 and the low-part {@code xx} has an opposite
+     *      signed non-zero magnitude then fraction high-part {@code f} will be {@code +/-1} such that
+     *      the double-double number is in the range {@code [0.5, 1)}.
+     * </ul>
+     *
+     * <p>This is named using the equivalent function in the standard C math.h library.
+     *
+     * @param exp Power of two scale factor (integral exponent).
+     * @return Fraction part.
+     * @see Math#getExponent(double)
+     * @see #scalb(int)
+     * @see <a href="https://www.cplusplus.com/reference/cmath/frexp/">C math.h frexp</a>
+     */
+    public DD frexp(int[] exp) {
+        exp[0] = getScale(x);
+        // Handle non-scalable numbers
+        if (exp[0] == Double.MAX_EXPONENT + 1) {
+            // Returns +/-0.0, inf or nan
+            // Maintain the fractional part unchanged.
+            // Do not change the fractional part of inf/nan, and assume
+            // |xx| < |x| thus if x == 0 then xx == 0 (otherwise the double-double is invalid)
+            // Unspecified for NaN/inf so just return zero exponent.
+            exp[0] = 0;
+            return this;
+        }
+        // The scale will create the fraction in [1, 2) so increase by 1 for [0.5, 1)
+        exp[0] += 1;
+        DD f = scalb(-exp[0]);
+        // Return |(hi, lo)| = (1, -eps) if required.
+        // f.x * f.xx < 0 detects sign change unless the product underflows.
+        // Handle extreme case of |f.xx| being min value by doubling f.x to 1.
+        if (Math.abs(f.x) == HALF && 2 * f.x * f.xx < 0) {
+            f = new DD(f.x * 2, f.xx * 2);
+            exp[0] -= 1;
+        }
+        return f;
+    }
+
+    /**
+     * Returns a scale suitable for use with {@link Math#scalb(double, int)} to normalise
+     * the number to the interval {@code [1, 2)}.
+     *
+     * <p>In contrast to {@link Math#getExponent(double)} this handles
+     * sub-normal numbers by computing the number of leading zeros in the mantissa
+     * and shifting the unbiased exponent. The result is that for all finite, non-zero,
+     * numbers, the magnitude of {@code scalb(x, -getScale(x))} is
+     * always in the range {@code [1, 2)}.
+     *
+     * <p>This method is a functional equivalent of the c function ilogb(double).
+     *
+     * <p>The result is to be used to scale a number using {@link Math#scalb(double, int)}.
+     * Hence the special case of a zero argument is handled using the return value for NaN
+     * as zero cannot be scaled. This is different from {@link Math#getExponent(double)}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If the argument is NaN or infinite, then the result is {@link Double#MAX_EXPONENT} + 1.
+     *  <li>If the argument is zero, then the result is {@link Double#MAX_EXPONENT} + 1.
+     * </ul>
+     *
+     * @param a Value.
+     * @return The unbiased exponent of the value to be used for scaling, or 1024 for 0, NaN or Inf
+     * @see Math#getExponent(double)
+     * @see Math#scalb(double, int)
+     * @see <a href="https://www.cplusplus.com/reference/cmath/ilogb/">ilogb</a>
+     */
+    private static int getScale(double a) {
+        // Only interested in the exponent and mantissa so remove the sign bit
+        final long bits = Double.doubleToRawLongBits(a) & UNSIGN_MASK;
+        // Get the unbiased exponent
+        int exp = ((int) (bits >>> 52)) - EXPONENT_OFFSET;
+
+        // No case to distinguish nan/inf (exp == 1024).
+        // Handle sub-normal numbers
+        if (exp == Double.MIN_EXPONENT - 1) {
+            // Special case for zero, return as nan/inf to indicate scaling is not possible
+            if (bits == 0) {
+                return Double.MAX_EXPONENT + 1;
+            }
+            // A sub-normal number has an exponent below -1022. The amount below
+            // is defined by the number of shifts of the most significant bit in
+            // the mantissa that is required to get a 1 at position 53 (i.e. as
+            // if it were a normal number with assumed leading bit)
+            final long mantissa = bits & MANTISSA_MASK;
+            exp -= Long.numberOfLeadingZeros(mantissa << 12);
+        }
+        return exp;
+    }
+
+    /**
+     * Compute {@code this} number {@code (x, xx)} raised to the power {@code n}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code x} is not a finite normalized {@code double}, the low part {@code xx}
+     *      is ignored and the result is {@link Math#pow(double, double) Math.pow(x, n)}.
+     *  <li>If {@code n = 0} the result is {@code (1, 0)}.
+     *  <li>If {@code n = 1} the result is {@code (x, xx)}.
+     *  <li>If {@code n = -1} the result is the {@link #reciprocal() reciprocal}.
+     *  <li>If the computation overflows the result is undefined.
+     * </ul>
+     *
+     * <p>Computation uses multiplication by factors generated by repeat squaring of the value.
+     * These multiplications have no special case handling for overflow; in the event of overflow
+     * the result is undefined. The {@link #pow(int, long[])} method can be used to
+     * generate a scaled fraction result for any finite {@code DD} number and exponent.
+     *
+     * <p>The computed result is approximately {@code 16 * (n - 1) * eps} of the exact result
+     * where eps is 2<sup>-106</sup>.
+     *
+     * @param n Exponent.
+     * @return {@code this}<sup>n</sup>
+     * @see Math#pow(double, double)
+     * @see #pow(int, long[])
+     * @see #isFinite()
+     */
+    @Override
+    public DD pow(int n) {
+        // Edge cases.
+        if (n == 1) {
+            return this;
+        }
+        if (n == 0) {
+            return ONE;
+        }
+
+        // Handles {infinity, nan and zero} cases
+        if (isNotNormal(x)) {
+            // Assume the high part has the greatest magnitude
+            // so here the low part is irrelevant
+            return new DD(Math.pow(x, n), 0);
+        }
+
+        // Here hi is finite; assume lo is also finite
+        if (n == -1) {
+            return reciprocal();
+        }
+
+        // Extended precision computation is required.
+        // No checks for overflow.
+        if (n < 0) {
+            // Note: Correctly handles negating -2^31
+            return computePow(x, xx, -n).reciprocal();
+        }
+        return computePow(x, xx, n);
+    }
+
+    /**
+     * Compute the number {@code x} (non-zero finite) raised to the power {@code n}.
+     *
+     * <p>The input power is treated as an unsigned integer. Thus the negative value
+     * {@link Integer#MIN_VALUE} is 2^31.
+     *
+     * @param x Fractional high part of x.
+     * @param xx Fractional low part of x.
+     * @param n Power (in [2, 2^31]).
+     * @return x^n.
+     */
+    private static DD computePow(double x, double xx, int n) {
+        // Compute the power by multiplication (keeping track of the scale):
+        // 13 = 1101
+        // x^13 = x^8 * x^4 * x^1
+        //      = ((x^2 * x)^2)^2 * x
+        // 21 = 10101
+        // x^21 = x^16 * x^4 * x^1
+        //      = (((x^2)^2 * x)^2)^2 * x
+        // 1. Find highest set bit in n (assume n != 0)
+        // 2. Initialise result as x
+        // 3. For remaining bits (0 or 1) below the highest set bit:
+        //    - square the current result
+        //    - if the current bit is 1 then multiply by x
+        // In this scheme the factors to multiply by x can be pre-computed.
+
+        // Split b
+        final double xh = highPart(x);
+        final double xl = x - xh;
+
+        // Initialise the result as x^1
+        double f0 = x;
+        double f1 = xx;
+
+        double u;
+        double v;
+        double w;
+
+        // Shift the highest set bit off the top.
+        // Any remaining bits are detected in the sign bit.
+        final int shift = Integer.numberOfLeadingZeros(n) + 1;
+        int bits = n << shift;
+
+        // Multiplication is done without object allocation of DD intermediates.
+        // The square can be optimised.
+        // Process remaining bits below highest set bit.
+        for (int i = 32 - shift; i != 0; i--, bits <<= 1) {
+            // Square the result
+            // Inline multiply(f0, f1, f0, f1), adapted for squaring
+            u = f0 * f0;
+            v = twoSquareLow(f0, u);
+            // Inline (f0, f1) = fastTwoSum(hi, lo + (2 * f0 * f1))
+            w = v + (2 * f0 * f1);
+            f0 = u + w;
+            f1 = fastTwoSumLow(u, w, f0);
+            if (bits < 0) {
+                // Inline multiply(f0, f1, x, xx)
+                u = highPart(f0);
+                v = f0 - u;
+                w = f0 * x;
+                v = twoProductLow(u, v, xh, xl, w);
+                // Inline (f0, f1) = fastTwoSum(w, v + (f0 * xx + f1 * x))
+                u = v + (f0 * xx + f1 * x);
+                f0 = w + u;
+                f1 = fastTwoSumLow(w, u, f0);
+            }
+        }
+
+        return new DD(f0, f1);
+    }
+
+    /**
+     * Compute {@code this} number {@code x} raised to the power {@code n}.
+     *
+     * <p>The value is returned as fractional {@code f} and integral
+     * {@code 2^exp} components.
+     * <pre>
+     * (x+xx)^n = (f+ff) * 2^exp
+     * </pre>
+     *
+     * <p>The combined fractional part (f, ff) is in the range {@code [0.5, 1)}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code (x, xx)} is zero the high part of the fractional part is
+     *      computed using {@link Math#pow(double, double) Math.pow(x, n)} and the exponent is 0.
+     *  <li>If {@code n = 0} the fractional part is 0.5 and the exponent is 1.
+     *  <li>If {@code (x, xx)} is an exact power of 2 the fractional part is 0.5 and the exponent
+     *      is the power of 2 minus 1.
+     *  <li>If the result high-part is an exact power of 2 and the low-part has an opposite
+     *      signed non-zero magnitude then the fraction high-part {@code f} will be {@code +/-1} such that
+     *      the double-double number is in the range {@code [0.5, 1)}.
+     *  <li>If the argument is not finite then a fractional representation is not possible.
+     *      In this case the fraction and the scale factor is undefined.
+     * </ul>
+     *
+     * <p>The computed result is approximately {@code 16 * (n - 1) * eps} of the exact result
+     * where eps is 2<sup>-106</sup>.
+     *
+     * @param n Power.
+     * @param exp Result power of two scale factor (integral exponent).
+     * @return Fraction part.
+     * @see #frexp(int[])
+     */
+    public DD pow(int n, long[] exp) {
+        // Edge cases.
+        if (n == 0) {
+            exp[0] = 1;
+            return new DD(0.5, 0);
+        }
+        // IEEE result for non-finite or zero
+        if (!Double.isFinite(x) || x == 0) {
+            exp[0] = 0;
+            return new DD(Math.pow(x, n), 0);
+        }
+        // Here the number is non-zero finite
+        final int[] ie = {0};
+        DD f = frexp(ie);
+        final long b = ie[0];
+        // Handle exact powers of 2
+        if (Math.abs(f.x) == HALF && f.xx == 0) {
+            // (f * 2^b)^n = (2f)^n * 2^(b-1)^n
+            // Use Math.pow to create the sign.
+            // Note the result must be scaled to the fractional representation
+            // by multiplication by 0.5 and addition of 1 to the exponent.
+            final double y0 = 0.5 * Math.pow(2 * f.x, n);
+            // Propagate sign change (y0*f.x) to the original zero (this.xx)
+            final double y1 = Math.copySign(0.0, y0 * f.x * this.xx);
+            exp[0] = 1 + (b - 1) * n;
+            return new DD(y0, y1);
+        }
+        if (n < 0) {
+            f = computePowScaled(b, f.x, f.xx, -n, exp);
+            // Result is a non-zero fraction part so inversion is safe
+            f = reciprocal(f.x, f.xx);
+            // Rescale to [0.5, 1.0)
+            f = f.frexp(ie);
+            exp[0] = ie[0] - exp[0];
+            return f;
+        }
+        return computePowScaled(b, f.x, f.xx, n, exp);
+    }
+
+    /**
+     * Compute the number {@code x} (non-zero finite) raised to the power {@code n}.
+     *
+     * <p>The input power is treated as an unsigned integer. Thus the negative value
+     * {@link Integer#MIN_VALUE} is 2^31.
+     *
+     * @param b Integral component 2^exp of x.
+     * @param x Fractional high part of x.
+     * @param xx Fractional low part of x.
+     * @param n Power (in [2, 2^31]).
+     * @param exp Result power of two scale factor (integral exponent).
+     * @return Fraction part.
+     */
+    private static DD computePowScaled(long b, double x, double xx, int n, long[] exp) {
+        // Compute the power by multiplication (keeping track of the scale):
+        // 13 = 1101
+        // x^13 = x^8 * x^4 * x^1
+        //      = ((x^2 * x)^2)^2 * x
+        // 21 = 10101
+        // x^21 = x^16 * x^4 * x^1
+        //      = (((x^2)^2 * x)^2)^2 * x
+        // 1. Find highest set bit in n (assume n != 0)
+        // 2. Initialise result as x
+        // 3. For remaining bits (0 or 1) below the highest set bit:
+        //    - square the current result
+        //    - if the current bit is 1 then multiply by x
+        // In this scheme the factors to multiply by x can be pre-computed.
+
+        // Scale the input in [0.5, 1) to be above 1. Represented as 2^be * b.
+        final long be = b - 1;
+        final double b0 = x * 2;
+        final double b1 = xx * 2;
+        // Split b
+        final double b0h = highPart(b0);
+        final double b0l = b0 - b0h;
+
+        // Initialise the result as x^1. Represented as 2^fe * f.
+        long fe = be;
+        double f0 = b0;
+        double f1 = b1;
+
+        double u;
+        double v;
+        double w;
+
+        // Shift the highest set bit off the top.
+        // Any remaining bits are detected in the sign bit.
+        final int shift = Integer.numberOfLeadingZeros(n) + 1;
+        int bits = n << shift;
+
+        // Multiplication is done without using DD.multiply as the arguments
+        // are always finite and the product will not overflow. The square can be optimised.
+        // Process remaining bits below highest set bit.
+        for (int i = 32 - shift; i != 0; i--, bits <<= 1) {
+            // Square the result
+            // Inline multiply(f0, f1, f0, f1, f), adapted for squaring
+            fe <<= 1;
+            u = f0 * f0;
+            v = twoSquareLow(f0, u);
+            // Inline fastTwoSum(hi, lo + (2 * f0 * f1), f)
+            w = v + (2 * f0 * f1);
+            f0 = u + w;
+            f1 = fastTwoSumLow(u, w, f0);
+            // Rescale
+            if (Math.abs(f0) > SAFE_MULTIPLY) {
+                // Scale back to the [1, 2) range. As safe multiply is 2^500
+                // the exponent should be < 1001 so the twoPow scaling factor is supported.
+                final int e = Math.getExponent(f0);
+                final double s = twoPow(-e);
+                fe += e;
+                f0 *= s;
+                f1 *= s;
+            }
+            if (bits < 0) {
+                // Multiply by b
+                fe += be;
+                // Inline multiply(f0, f1, b0, b1, f)
+                u = highPart(f0);
+                v = f0 - u;
+                w = f0 * b0;
+                v = twoProductLow(u, v, b0h, b0l, w);
+                // Inline fastTwoSum(w, v + (f0 * b1 + f1 * b0), f)
+                u = v + (f0 * b1 + f1 * b0);
+                f0 = w + u;
+                f1 = fastTwoSumLow(w, u, f0);
+                // Avoid rescale as x2 is in [1, 2)
+            }
+        }
+
+        final int[] e = {0};
+        final DD f = new DD(f0, f1).frexp(e);
+        exp[0] = fe + e[0];
+        return f;
+    }
+
+    /**
+     * Test for equality with another object. If the other object is a {@code DD} then a
+     * comparison is made of the parts; otherwise {@code false} is returned.
+     *
+     * <p>If both parts of two double-double numbers
+     * are exactly the same the two {@code DD} objects are considered to be equal.
+     * For this purpose, two {@code double} values are considered to be
+     * the same if and only if the method {@link Double #doubleToLongBits(double)}
+     * returns the identical {@code long} value when applied to each.
+     *
+     * <p>Note that in most cases, for two instances of class
+     * {@code DD}, {@code x} and {@code y}, the
+     * value of {@code x.equals(y)} is {@code true} if and only if
+     *
+     * <pre>
+     *  {@code x.hi()() == y.hi() && x.lo() == y.lo()}</pre>
+     *
+     * <p>also has the value {@code true}. However, there are exceptions:
+     *
+     * <ul>
+     *  <li>Instances that contain {@code NaN} values in the same part
+     *      are considered to be equal for that part, even though {@code Double.NaN == Double.NaN}
+     *      has the value {@code false}.
+     *  <li>Instances that share a {@code NaN} value in one part
+     *      but have different values in the other part are <em>not</em> considered equal.
+     *  <li>Instances that contain different representations of zero in the same part
+     *      are <em>not</em> considered to be equal for that part, even though {@code -0.0 == 0.0}
+     *      has the value {@code true}.
+     * </ul>
+     *
+     * <p>The behavior is the same as if the components of the two double-double numbers were passed
+     * to {@link java.util.Arrays#equals(double[], double[]) Arrays.equals(double[], double[])}:
+     *
+     * <pre>
+     *  Arrays.equals(new double[]{x.hi(), x.lo()},
+     *                new double[]{y.hi(), y.lo()}); </pre>
+     *
+     * @param other Object to test for equality with this instance.
+     * @return {@code true} if the objects are equal, {@code false} if object
+     * is {@code null}, not an instance of {@code DD}, or not equal to
+     * this instance.
+     * @see Double#doubleToLongBits(double)
+     * @see java.util.Arrays#equals(double[], double[])
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof DD) {
+            final DD c = (DD) other;
+            return equals(x, c.x) && equals(xx, c.xx);
+        }
+        return false;
+    }
+
+    /**
+     * Gets a hash code for the double-double number.
+     *
+     * <p>The behavior is the same as if the parts of the double-double number were passed
+     * to {@link java.util.Arrays#hashCode(double[]) Arrays.hashCode(double[])}:
+     *
+     * <pre>
+     *  {@code Arrays.hashCode(new double[] {hi(), lo()})}</pre>
+     *
+     * @return A hash code value for this object.
+     * @see java.util.Arrays#hashCode(double[]) Arrays.hashCode(double[])
+     */
+    @Override
+    public int hashCode() {
+        return 31 * (31 + Double.hashCode(x)) + Double.hashCode(xx);
+    }
+
+    /**
+     * Returns {@code true} if the values are equal according to semantics of
+     * {@link Double#equals(Object)}.
+     *
+     * @param x Value
+     * @param y Value
+     * @return {@code Double.valueof(x).equals(Double.valueOf(y))}.
+     */
+    private static boolean equals(double x, double y) {
+        return Double.doubleToLongBits(x) == Double.doubleToLongBits(y);
+    }
+
+    /**
+     * Returns a string representation of the double-double number.
+     *
+     * <p>The string will represent the numeric values of the parts.
+     * The values are split by a separator and surrounded by parentheses.
+     *
+     * <p>The format for a double-double number is {@code "(x,xx)"}, with {@code x} and
+     * {@code xx} converted as if using {@link Double#toString(double)}.
+     *
+     * <p>Note: A numerical string representation of a finite double-double number can be
+     * generated by conversion to a {@link BigDecimal} before formatting.
+     *
+     * @return A string representation of the double-double number.
+     * @see Double#toString(double)
+     * @see #bigDecimalValue()
+     */
+    @Override
+    public String toString() {
+        return new StringBuilder(TO_STRING_SIZE)
+            .append(FORMAT_START)
+            .append(x).append(FORMAT_SEP)
+            .append(xx)
+            .append(FORMAT_END)
+            .toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Note: Addition of this value with any element {@code a} may not create an
+     * element equal to {@code a} if the element contains sign zeros. In this case the
+     * magnitude of the result will be identical.
+     */
+    @Override
+    public DD zero() {
+        return ZERO;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Note: Multiplication of this value with any element {@code a} may not create an
+     * element equal to {@code a} if the element contains sign zeros. In this case the
+     * magnitude of the result will be identical.
+     */
+    @Override
+    public DD one() {
+        return ONE;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This computes the same result as {@link #multiply(double) multiply((double) y)}.
+     *
+     * @see #multiply(double)
+     */
+    @Override
+    public DD multiply(int n) {
+        // Note: This method exists to support the NativeOperators interface
+        return multiply(x, xx, n);
+    }
+}
diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DDMath.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DDMath.java
new file mode 100644
index 00000000..8d3b36d5
--- /dev/null
+++ b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/DDMath.java
@@ -0,0 +1,480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.core;
+
+/**
+ * Computes extended precision floating-point operations.
+ *
+ * <p>This class supplements the arithmetic operations in the {@link DD} class providing
+ * greater accuracy at the cost of performance.
+ *
+ * @since 1.2
+ */
+public final class DDMath {
+    /** 0.5. */
+    private static final double HALF = 0.5;
+    /** The limit for safe multiplication of {@code x*y}, assuming values above 1.
+     * Used to maintain positive values during the power computation. */
+    private static final double SAFE_MULTIPLY = 0x1.0p500;
+
+    /**
+     * Mutable double-double number used for working.
+     * This structure is used for the output argument during triple-double computations.
+     */
+    private static final class MDD {
+        /** The high part of the double-double number. */
+        private double x;
+        /** The low part of the double-double number. */
+        private double xx;
+    }
+
+    /** No instances. */
+    private DDMath() {}
+
+    /**
+     * Compute the number {@code x} raised to the power {@code n}.
+     *
+     * <p>The value is returned as fractional {@code f} and integral
+     * {@code 2^exp} components.
+     * <pre>
+     * (x+xx)^n = (f+ff) * 2^exp
+     * </pre>
+     *
+     * <p>The combined fractional part (f, ff) is in the range {@code [0.5, 1)}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code (x, xx)} is zero the high part of the fractional part is
+     *      computed using {@link Math#pow(double, double) Math.pow(x, n)} and the exponent is 0.
+     *  <li>If {@code n = 0} the fractional part is 0.5 and the exponent is 1.
+     *  <li>If {@code (x, xx)} is an exact power of 2 the fractional part is 0.5 and the exponent
+     *      is the power of 2 minus 1.
+     *  <li>If the result high-part is an exact power of 2 and the low-part has an opposite
+     *      signed non-zero magnitude then the fraction high-part {@code f} will be {@code +/-1} such that
+     *      the double-double number is in the range {@code [0.5, 1)}.
+     *  <li>If the argument is not finite then a fractional representation is not possible.
+     *      In this case the fraction and the scale factor is undefined.
+     * </ul>
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 4-fold slower than {@link DD#pow(int, long[])}.
+     *
+     * @param x Number.
+     * @param n Power.
+     * @param exp Result power of two scale factor (integral exponent).
+     * @return Fraction part.
+     * @see DD#frexp(int[])
+     */
+    public static DD pow(DD x, int n, long[] exp) {
+        // Edge cases.
+        if (n == 0) {
+            exp[0] = 1;
+            return DD.of(0.5);
+        }
+        // IEEE result for non-finite or zero
+        if (!Double.isFinite(x.hi()) || x.hi() == 0) {
+            exp[0] = 0;
+            return DD.of(Math.pow(x.hi(), n));
+        }
+        // Here the number is non-zero finite
+        final int[] ie = {0};
+        final DD f = x.frexp(ie);
+        final long b = ie[0];
+        // Handle exact powers of 2
+        if (Math.abs(f.hi()) == HALF && f.lo() == 0) {
+            // (f * 2^b)^n = (2f)^n * 2^(b-1)^n
+            // Use Math.pow to create the sign.
+            // Note the result must be scaled to the fractional representation
+            // by multiplication by 0.5 and addition of 1 to the exponent.
+            final double y0 = 0.5 * Math.pow(2 * f.hi(), n);
+            // Propagate sign change (y0*f.x) to the original zero (this.xx)
+            final double y1 = Math.copySign(0.0, y0 * f.hi() * x.lo());
+            exp[0] = 1 + (b - 1) * n;
+            return DD.of(y0, y1);
+        }
+        return computePowScaled(b, f.hi(), f.lo(), n, exp);
+    }
+
+    /**
+     * Compute the number {@code x} (non-zero finite) raised to the power {@code n}.
+     *
+     * <p>Performs the computation using triple-double precision. If the input power is
+     * negative the result is computed using the absolute value of {@code n} and then
+     * inverted by dividing into 1.
+     *
+     * @param b Integral component 2^b of x.
+     * @param x Fractional high part of x.
+     * @param xx Fractional low part of x.
+     * @param n Power (in [2, 2^31]).
+     * @param exp Result power of two scale factor (integral exponent).
+     * @return Fraction part.
+     */
+    private static DD computePowScaled(long b, double x, double xx, int n, long[] exp) {
+        // Same as DD.computePowScaled using a triple-double intermediate.
+
+        // triple-double multiplication:
+        // (a0, a1, a2) * (b0, b1, b2)
+        // a x b ~ a0b0                 O(1) term
+        //       + a0b1 + a1b0          O(eps) terms
+        //       + a0b2 + a1b1 + a2b0   O(eps^2) terms
+        //       + a1b2 + a2b1          O(eps^3) terms
+        //       + a2b2                 O(eps^4) term  (not required for the first 159 bits)
+        // Higher terms require two-prod if the round-off is <= O(eps^3).
+        // (pij,qij) = two-prod(ai, bj); pij = O(eps^i+j); qij = O(eps^i+j+1)
+        // p00                      O(1)
+        // p01, p10, q00            O(eps)
+        // p02, p11, p20, q01, q10  O(eps^2)
+        // p12, p21, q02, q11, q20  O(eps^3)
+        // Sum terms of the same order. Carry round-off to lower order:
+        // s0 = p00                                        Order(1)
+        // Sum (p01, p10, q00) -> (s1, r2, r3a)            Order(eps)
+        // Sum (p02, p11, p20, q01, q10, r2) -> (s2, r3b)  Order(eps^2)
+        // Sum (p12, p21, q02, q11, q20, r3a, r3b) -> s3   Order(eps^3)
+        //
+        // Simplifies for (b0, b1):
+        // Sum (p01, p10, q00) -> (s1, r2, r3a)            Order(eps)
+        // Sum (p11, p20, q01, q10, r2) -> (s2, r3b)       Order(eps^2)
+        // Sum (p21, q11, q20, r3a, r3b) -> s3             Order(eps^3)
+        //
+        // Simplifies for the square:
+        // Sum (2 * p01, q00) -> (s1, r2)                  Order(eps)
+        // Sum (2 * p02, 2 * q01, p11, r2) -> (s2, r3b)    Order(eps^2)
+        // Sum (2 * p12, 2 * q02, q11, r3b) -> s3          Order(eps^3)
+
+        // Scale the input in [0.5, 1) to be above 1. Represented as 2^be * b.
+        final long be = b - 1;
+        final double b0 = x * 2;
+        final double b1 = xx * 2;
+        // Split b
+        final double b0h = DD.highPart(b0);
+        final double b0l = b0 - b0h;
+        final double b1h = DD.highPart(b1);
+        final double b1l = b1 - b1h;
+
+        // Initialise the result as x^1. Represented as 2^fe * f.
+        long fe = be;
+        double f0 = b0;
+        double f1 = b1;
+        double f2 = 0;
+
+        // Shift the highest set bit off the top.
+        // Any remaining bits are detected in the sign bit.
+        final int an = Math.abs(n);
+        final int shift = Integer.numberOfLeadingZeros(an) + 1;
+        int bits = an << shift;
+        DD t;
+        final MDD m = new MDD();
+
+        // Multiplication is done inline with some triple precision helper routines.
+        // Process remaining bits below highest set bit.
+        for (int i = 32 - shift; i != 0; i--, bits <<= 1) {
+            // Square the result
+            fe <<= 1;
+            double a0h = DD.highPart(f0);
+            double a0l = f0 - a0h;
+            double a1h = DD.highPart(f1);
+            double a1l = f1 - a1h;
+            double a2h = DD.highPart(f2);
+            double a2l = f2 - a2h;
+            double p00 = f0 * f0;
+            double q00 = DD.twoSquareLow(a0h, a0l, p00);
+            double p01 = f0 * f1;
+            double q01 = DD.twoProductLow(a0h, a0l, a1h, a1l, p01);
+            final double p02 = f0 * f2;
+            final double q02 = DD.twoProductLow(a0h, a0l, a2h, a2l, p02);
+            double p11 = f1 * f1;
+            double q11 = DD.twoSquareLow(a1h, a1l, p11);
+            final double p12 = f1 * f2;
+            double s0 = p00;
+            // Sum (2 * p01, q00) -> (s1, r2)                  Order(eps)
+            double s1 = 2 * p01 + q00;
+            double r2 = DD.twoSumLow(2 * p01, q00, s1);
+            // Sum (2 * p02, 2 * q01, p11, r2) -> (s2, r3b)    Order(eps^2)
+            double s2 = p02 + q01;
+            double r3b = DD.twoSumLow(p02, q01, s2);
+            double u = p11 + r2;
+            double v = DD.twoSumLow(p11, r2, u);
+            t = DD.add(2 * s2, 2 * r3b, u, v);
+            s2 = t.hi();
+            r3b = t.lo();
+            // Sum (2 * p12, 2 * q02, q11, r3b) -> s3          Order(eps^3)
+            double s3 = 2 * (p12 + q02) + q11 + r3b;
+            f0 = norm3(s0, s1, s2, s3, m);
+            f1 = m.x;
+            f2 = m.xx;
+
+            // Rescale
+            if (Math.abs(f0) > SAFE_MULTIPLY) {
+                // Scale back to the [1, 2) range. As safe multiply is 2^500
+                // the exponent should be < 1001 so the twoPow scaling factor is supported.
+                final int e = Math.getExponent(f0);
+                final double s = DD.twoPow(-e);
+                fe += e;
+                f0 *= s;
+                f1 *= s;
+                f2 *= s;
+            }
+
+            if (bits < 0) {
+                // Multiply by b
+                fe += be;
+                a0h = DD.highPart(f0);
+                a0l = f0 - a0h;
+                a1h = DD.highPart(f1);
+                a1l = f1 - a1h;
+                a2h = DD.highPart(f2);
+                a2l = f2 - a2h;
+                p00 = f0 * b0;
+                q00 = DD.twoProductLow(a0h, a0l, b0h, b0l, p00);
+                p01 = f0 * b1;
+                q01 = DD.twoProductLow(a0h, a0l, b1h, b1l, p01);
+                final double p10 = f1 * b0;
+                final double q10 = DD.twoProductLow(a1h, a1l, b0h, b0l, p10);
+                p11 = f1 * b1;
+                q11 = DD.twoProductLow(a1h, a1l, b1h, b1l, p11);
+                final double p20 = f2 * b0;
+                final double q20 = DD.twoProductLow(a2h, a2l, b0h, b0l, p20);
+                final double p21 = f2 * b1;
+                s0 = p00;
+                // Sum (p01, p10, q00) -> (s1, r2, r3a)            Order(eps)
+                u = p01 + p10;
+                v = DD.twoSumLow(p01, p10, u);
+                s1 = q00 + u;
+                final double w = DD.twoSumLow(q00, u, s1);
+                r2 = v + w;
+                final double r3a = DD.twoSumLow(v, w, r2);
+                // Sum (p11, p20, q01, q10, r2) -> (s2, r3b)       Order(eps^2)
+                s2 = p11 + p20;
+                r3b = DD.twoSumLow(p11, p20, s2);
+                u = q01 + q10;
+                v = DD.twoSumLow(q01, q10, u);
+                t = DD.add(s2, r3b, u, v);
+                s2 = t.hi() + r2;
+                r3b = DD.twoSumLow(t.hi(), r2, s2);
+                // Sum (p21, q11, q20, r3a, r3b) -> s3             Order(eps^3)
+                s3 = p21 + q11 + q20 + r3a + r3b;
+                f0 = norm3(s0, s1, s2, s3, m);
+                f1 = m.x;
+                f2 = m.xx;
+                // Avoid rescale as x2 is in [1, 2)
+            }
+        }
+
+        // Ensure (f0, f1) are 1 ulp exact
+        final double u = f1 + f2;
+        t = DD.fastTwoSum(f0, u);
+        final int[] e = {0};
+
+        // If the power is negative, invert in triple precision
+        if (n < 0) {
+            // Require the round-off
+            final double v = DD.fastTwoSumLow(f1, f2, u);
+            // Result is in approximately [1, 2^501] so inversion is safe.
+            t = inverse3(t.hi(), t.lo(), v);
+            // Rescale to [0.5, 1.0]
+            t = t.frexp(e);
+            exp[0] = e[0] - fe;
+            return t;
+        }
+
+        t = t.frexp(e);
+        exp[0] = fe + e[0];
+        return t;
+    }
+
+    /**
+     * Normalize (s0, s1, s2, s3) to (s0, s1, s2).
+     *
+     * @param s0 High part of s.
+     * @param s1 Second part of s.
+     * @param s2 Third part of s.
+     * @param s3 Fourth part of s.
+     * @param s12 Output parts (s1, s2)
+     * @return s0
+     */
+    private static double norm3(double s0, double s1, double s2, double s3, MDD s12) {
+        double q;
+        // Compress (Schewchuk Fig. 15) (s0, s1, s2, s3) -> (g0, g1, g2, g3)
+        final double g0 = s0 + s1;
+        q = DD.fastTwoSumLow(s0, s1, g0);
+        final double g1 = q + s2;
+        q = DD.fastTwoSumLow(q, s2, g1);
+        final double g2 = q + s3;
+        final double g3 = DD.fastTwoSumLow(q, s3, g2);
+        // (g0, g1, g2, g3) -> (h0, h1, h2, h3), returned as (h0, h1, h2 + h3)
+        q = g1 + g2;
+        s12.xx = DD.fastTwoSumLow(g1, g2, q) + g3;
+        final double h0 = g0 + q;
+        s12.x = DD.fastTwoSumLow(g0, q, h0);
+        return h0;
+    }
+
+    /**
+     * Compute the inverse of {@code (y, yy, yyy)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>This is special routine used in {@link #pow(int, long[])}
+     * to invert the triple precision result.
+     *
+     * @param y First part of y.
+     * @param yy Second part of y.
+     * @param yyy Third part of y.
+     * @return the inverse
+     */
+    private static DD inverse3(double y, double yy, double yyy) {
+        // Long division (1, 0, 0) / (y, yy, yyy)
+        double r;
+        double rr;
+        double rrr;
+        double t;
+        // quotient q0 = x / y
+        final double q0 = 1 / y;
+        // remainder r0 = x - q0 * y
+        final MDD q = new MDD();
+        t = multiply3(y, yy, yyy, q0, q);
+        r = add3(-t, -q.x, -q.xx, 1, q);
+        rr = q.x;
+        rrr = q.xx;
+        // next quotient q1 = r0 / y
+        final double q1 = r / y;
+        // remainder r1 = r0 - q1 * y
+        t = multiply3(y, yy, yyy, q1, q);
+        r = add3(-t, -q.x, -q.xx, r, rr, rrr, q);
+        rr = q.x;
+        rrr = q.xx;
+        // next quotient q2 = r1 / y
+        final double q2 = r / y;
+        // remainder r2 = r1 - q2 * y
+        t = multiply3(y, yy, yyy, q2, q);
+        r = add3(-t, -q.x, -q.xx, r, rr, rrr, q);
+        // next quotient q3 = r2 / y
+        final double q3 = r / y;
+        // Collect (q0, q1, q2, q3) to (s0, s1, s2)
+        t = norm3(q0, q1, q2, q3, q);
+        // Reduce to (s0, s1)
+        return DD.fastTwoSum(t, q.x + q.xx);
+    }
+
+    /**
+     * Compute the multiplication product of {@code (a0,a1,a2)} and {@code b}.
+     *
+     * @param a0 High part of a.
+     * @param a1 Second part of a.
+     * @param a2 Third part of a.
+     * @param b Factor.
+     * @param s12 Output parts (s1, s2)
+     * @return s0
+     */
+    private static double multiply3(double a0, double a1, double a2, double b, MDD s12) {
+        // Triple-Double x Double
+        // a x b ~ a0b                 O(1) term
+        //       + a1b                 O(eps) terms
+        //       + a2b                 O(eps^2) terms
+        // Higher terms require two-prod if the round-off is <= O(eps^2).
+        // (pij,qij) = two-prod(ai, bj); pij = O(eps^i+j); qij = O(eps^i+j+1)
+        // p00           O(1)
+        // p10, q00      O(eps)
+        // p20, q10      O(eps^2)
+        // |a2| < |eps^2 a0| => |a2 * b| < eps^2 |a0 * b| and q20 < eps^3 |a0 * b|
+        //
+        // Sum terms of the same order. Carry round-off to lower order:
+        // s0 = p00                              Order(1)
+        // Sum (p10, q00) -> (s1, r1)            Order(eps)
+        // Sum (p20, q10, r1) -> (s2, s3)        Order(eps^2)
+        final double a0h = DD.highPart(a0);
+        final double a0l = a0 - a0h;
+        final double a1h = DD.highPart(a1);
+        final double a1l = a1 - a1h;
+        final double b0h = DD.highPart(b);
+        final double b0l = b - b0h;
+        final double p00 = a0 * b;
+        final double q00 = DD.twoProductLow(a0h, a0l, b0h, b0l, p00);
+        final double p10 = a1 * b;
+        final double q10 = DD.twoProductLow(a1h, a1l, b0h, b0l, p10);
+        final double p20 = a2 * b;
+        // Sum (p10, q00) -> (s1, r1)            Order(eps)
+        final double s1 = p10 + q00;
+        final double r1 = DD.twoSumLow(p10, q00, s1);
+        // Sum (p20, q10, r1) -> (s2, s3)        Order(eps^2)
+        double u = p20 + q10;
+        final double v = DD.twoSumLow(p20, q10, u);
+        final double s2 = u + r1;
+        u = DD.twoSumLow(u, r1, s2);
+        return norm3(p00, s1, s2, v + u, s12);
+    }
+
+    /**
+     * Compute the sum of {@code (a0,a1,a2)} and {@code b}.
+     *
+     * @param a0 High part of a.
+     * @param a1 Second part of a.
+     * @param a2 Third part of a.
+     * @param b Addend.
+     * @param s12 Output parts (s1, s2)
+     * @return s0
+     */
+    private static double add3(double a0, double a1, double a2, double b, MDD s12) {
+        // Hide et al (2008) Fig.5: Quad-Double + Double without final a3.
+        double u;
+        double v;
+        final double s0 = a0 + b;
+        u = DD.twoSumLow(a0, b, s0);
+        final double s1 = a1 + u;
+        v = DD.twoSumLow(a1, u, s1);
+        final double s2 = a2 + v;
+        u = DD.twoSumLow(a2, v, s2);
+        return norm3(s0, s1, s2, u, s12);
+    }
+
+    /**
+     * Compute the sum of {@code (a0,a1,a2)} and {@code (b0,b1,b2))}.
+     * It is assumed the absolute magnitudes of a and b are equal and the sign
+     * of a and b are opposite.
+     *
+     * @param a0 High part of a.
+     * @param a1 Second part of a.
+     * @param a2 Third part of a.
+     * @param b0 High part of b.
+     * @param b1 Second part of b.
+     * @param b2 Third part of b.
+     * @param s12 Output parts (s1, s2)
+     * @return s0
+     */
+    private static double add3(double a0, double a1, double a2, double b0, double b1, double b2, MDD s12) {
+        // Hide et al (2008) Fig.6: Quad-Double + Quad-Double without final a3, b3.
+        double u;
+        double v;
+        // a0 + b0 -> (s0, r1)
+        final double s0 = a0 + b0;
+        final double r1 = DD.twoSumLow(a0, b0, s0);
+        // a1 + b1 + r1 -> (s1, r2, r3)
+        u = a1 + b1;
+        v = DD.twoSumLow(a1, b1, u);
+        final double s1 = r1 + u;
+        u = DD.twoSumLow(r1, u, s1);
+        final double r2 = v + u;
+        final double r3 = DD.twoSumLow(v, u, r2);
+        // (a2 + b2 + r2) + r3 -> (s2, s3)
+        u = a2 + b2;
+        v = DD.twoSumLow(a2, b2, u);
+        final double s2 = r2 + u;
+        u = DD.twoSumLow(r2, u, s2);
+        final double s3 = v + u + r3;
+        return norm3(s0, s1, s2, s3, s12);
+    }
+}
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDExt.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDExt.java
new file mode 100644
index 00000000..133237c1
--- /dev/null
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDExt.java
@@ -0,0 +1,735 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.core;
+
+/**
+ * Computes double-double floating-point operations.
+ *
+ * <p>This class contains extension methods to supplement the functionality in {@link DD}.
+ * The methods are tested in {@link DDTest}. These include:
+ * <ul>
+ *  <li>Arithmetic operations that have 105+ bits of precision and are typically 2-3 bits more
+ *      accurate than the versions in {@link DD}.
+ *  <li>A power function based on {@link Math#pow(double, double)} with approximately 50+ bits of
+ *      precision.
+ * </ul>
+ *
+ * <p><b>Note</b>
+ *
+ * <p>This class is public and has public methods to allow testing within the examples JMH module.
+ *
+ * @since 1.2
+ */
+public final class DDExt {
+    /** Threshold for large n where the Taylor series for (1+z)^n is not applicable. */
+    private static final int LARGE_N = 100000000;
+    /** Threshold for (x, xx)^n where x=0.5 and low-part will not be sub-normal.
+     * Note x has an exponent of -1; xx of -54 (if normalized); the min normal exponent is -1022;
+     * add 10-bits headroom in case xx is below epsilon * x: 1022 - 54 - 10. 0.5^958 = 4.1e-289. */
+    private static final int SAFE_EXPONENT_F = 958;
+    /** Threshold for (x, xx)^n where n ~ 2^31 and low-part will not be sub-normal.
+     * x ~ exp(log(2^-958) / 2^31).
+     * Note: floor(-958 * ln(2) / ln(nextDown(SAFE_F))) < 2^31. */
+    private static final double SAFE_F = 0.9999996907846553;
+    /** Threshold for (x, xx)^n where x=2 and high-part is finite.
+     * For consistency we use 10-bits headroom down from max exponent 1023. 0.5^1013 = 8.78e304. */
+    private static final int SAFE_EXPONENT_2F = 1013;
+    /** Threshold for (x, xx)^n where n ~ 2^31 and high-part is finite.
+     * x ~ exp(log(2^1013) / 2^31)
+     * Note: floor(1013 * ln(2) / ln(nextUp(SAFE_2F))) < 2^31. */
+    private static final double SAFE_2F = 1.0000003269678954;
+    /** log(2) (20-digits). */
+    private static final double LN2 = 0.69314718055994530941;
+    /** sqrt(0.5) == 1 / sqrt(2). */
+    private static final double ROOT_HALF = 0.707106781186547524400;
+    /** The limit for safe multiplication of {@code x*y}, assuming values above 1.
+     * Used to maintain positive values during the power computation. */
+    private static final double SAFE_MULTIPLY = 0x1.0p500;
+    /** Used to downscale values before multiplication. Downscaling of any value
+     * strictly above SAFE_MULTIPLY will be above 1 even including a double-double
+     * roundoff that lowers the magnitude. */
+    private static final double SAFE_MULTIPLY_DOWNSCALE = 0x1.0p-500;
+
+    /**
+     * No instances.
+     */
+    private DDExt() {}
+
+    /**
+     * Compute the sum of {@code x} and {@code y}.
+     *
+     * <p>This computes the same result as
+     * {@link #add(DD, DD) add(x, DD.of(y))}.
+     *
+     * <p>The performance is approximately 1.5-fold slower than {@link DD#add(double)}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the sum
+     */
+    public static DD add(DD x, double y) {
+        return DD.accurateAdd(x.hi(), x.lo(), y);
+    }
+
+    /**
+     * Compute the sum of {@code x} and {@code y}.
+     *
+     * <p>The high-part of the result is within 1 ulp of the true sum {@code e}.
+     * The low-part of the result is within 1 ulp of the result of the high-part
+     * subtracted from the true sum {@code e - hi}.
+     *
+     * <p>The performance is approximately 2-fold slower than {@link DD#add(DD)}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the sum
+     */
+    public static DD add(DD x, DD y) {
+        return DD.accurateAdd(x.hi(), x.lo(), y.hi(), y.lo());
+    }
+
+    /**
+     * Compute the subtraction of {@code y} from {@code x}.
+     *
+     * <p>This computes the same result as
+     * {@link #add(DD, double) add(x, -y)}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the difference
+     */
+    public static DD subtract(DD x, double y) {
+        return DD.accurateAdd(x.hi(), x.lo(), -y);
+    }
+
+    /**
+     * Compute the subtraction of {@code y} from {@code x}.
+     *
+     * <p>This computes the same result as
+     * {@link #add(DD, DD) add(x, y.negate())}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the difference
+     */
+    public static DD subtract(DD x, DD y) {
+        return DD.accurateAdd(x.hi(), x.lo(), -y.hi(), -y.lo());
+    }
+
+    /**
+     * Compute the multiplication product of {@code x} and {@code y}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 2.5-fold slower than {@link DD#multiply(double)}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the product
+     */
+    public static DD multiply(DD x, double y) {
+        return accurateMultiply(x.hi(), x.lo(), y);
+    }
+
+    /**
+     * Compute the multiplication product of {@code (x, xx)} and {@code y}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 2.5-fold slower than {@link DD#multiply(double)}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y y.
+     * @return the product
+     */
+    private static DD accurateMultiply(double x, double xx, double y) {
+        // For working see accurateMultiply(double x, double xx, double y, double yy)
+
+        final double xh = DD.highPart(x);
+        final double xl = x - xh;
+        final double xxh = DD.highPart(xx);
+        final double xxl = xx - xxh;
+        final double yh = DD.highPart(y);
+        final double yl = y - yh;
+
+        final double p00 = x * y;
+        final double q00 = DD.twoProductLow(xh, xl, yh, yl, p00);
+        final double p10 = xx * y;
+        final double q10 = DD.twoProductLow(xxh, xxl, yh, yl, p10);
+
+        // The code below collates the O(eps) terms with a round-off
+        // so O(eps^2) terms can be added to it.
+
+        final double s0 = p00;
+        // Sum (p10, q00) -> (s1, r2)       Order(eps)
+        final double s1 = p10 + q00;
+        final double r2 = DD.twoSumLow(p10, q00, s1);
+
+        // Collect (s0, s1, r2 + q10)
+        final double u = s0 + s1;
+        final double v = DD.fastTwoSumLow(s0, s1, u);
+        return DD.fastTwoSum(u, r2 + q10 + v);
+    }
+
+    /**
+     * Compute the multiplication product of {@code x} and {@code y}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 4.5-fold slower than {@link DD#multiply(DD)}.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the product
+     */
+    public static DD multiply(DD x, DD y) {
+        return accurateMultiply(x.hi(), x.lo(), y.hi(), y.lo());
+    }
+
+    /**
+     * Compute the multiplication product of {@code (x, xx)} and {@code (y, yy)}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 4.5-fold slower than {@link DD#multiply(DD)}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the product
+     */
+    private static DD accurateMultiply(double x, double xx, double y, double yy) {
+        // double-double multiplication:
+        // (a0, a1) * (b0, b1)
+        // a x b ~ a0b0                 O(1) term
+        //       + a0b1 + a1b0          O(eps) terms
+        //       + a1b1                 O(eps^2) term
+        // Higher terms require two-prod if the round-off is <= O(eps^2).
+        // (pij,qij) = two-prod(ai, bj); pij = O(eps^i+j); qij = O(eps^i+j+1)
+        // p00               O(1)
+        // p01, p10, q00     O(eps)
+        // p11, q01, q10     O(eps^2)
+        // q11               O(eps^3)   (not required for the first 106 bits)
+        // Sum terms of the same order. Carry round-off to lower order:
+        // s0 = p00                              Order(1)
+        // Sum (p01, p10, q00) -> (s1, r2)       Order(eps)
+        // Sum (p11, q01, q10, r2) -> s2         Order(eps^2)
+
+        final double xh = DD.highPart(x);
+        final double xl = x - xh;
+        final double xxh = DD.highPart(xx);
+        final double xxl = xx - xxh;
+        final double yh = DD.highPart(y);
+        final double yl = y - yh;
+        final double yyh = DD.highPart(yy);
+        final double yyl = yy - yyh;
+
+        final double p00 = x * y;
+        final double q00 = DD.twoProductLow(xh, xl, yh, yl, p00);
+        final double p01 = x * yy;
+        final double q01 = DD.twoProductLow(xh, xl, yyh, yyl, p01);
+        final double p10 = xx * y;
+        final double q10 = DD.twoProductLow(xxh, xxl, yh, yl, p10);
+        final double p11 = xx * yy;
+
+        // Note: Directly adding same order terms (error = 2 eps^2):
+        // DD.fastTwoSum(p00, (p11 + q01 + q10) + (p01 + p10 + q00))
+
+        // The code below collates the O(eps) terms with a round-off
+        // so O(eps^2) terms can be added to it.
+
+        final double s0 = p00;
+        // Sum (p01, p10, q00) -> (s1, r2)       Order(eps)
+        double u = p01 + p10;
+        double v = DD.twoSumLow(p01, p10, u);
+        final double s1 = q00 + u;
+        final double w = DD.twoSumLow(q00, u, s1);
+        final double r2 = v + w;
+
+        // Collect (s0, s1, r2 + p11 + q01 + q10)
+        u = s0 + s1;
+        v = DD.fastTwoSumLow(s0, s1, u);
+        return DD.fastTwoSum(u, r2 + p11 + q01 + q10 + v);
+    }
+
+    /**
+     * Compute the square of {@code x}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 4.5-fold slower than {@link DD#square()}.
+     *
+     * @param x x.
+     * @return the square
+     */
+    public static DD square(DD x) {
+        return accurateSquare(x.hi(), x.lo());
+    }
+
+    /**
+     * Compute the square of {@code (x, xx)}.
+     *
+     * <p>The computed result is within 0.5 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 4.5-fold slower than {@link DD#square()}.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @return the square
+     */
+    private static DD accurateSquare(double x, double xx) {
+        // For working see accurateMultiply(double x, double xx, double y, double yy)
+
+        final double xh = DD.highPart(x);
+        final double xl = x - xh;
+        final double xxh = DD.highPart(xx);
+        final double xxl = xx - xxh;
+
+        final double p00 = x * x;
+        final double q00 = DD.twoSquareLow(xh, xl, p00);
+        final double p01 = x * xx;
+        final double q01 = DD.twoProductLow(xh, xl, xxh, xxl, p01);
+        final double p11 = xx * xx;
+
+        // The code below collates the O(eps) terms with a round-off
+        // so O(eps^2) terms can be added to it.
+
+        final double s0 = p00;
+        // Sum (p01, p10, q00) -> (s1, r2)       Order(eps)
+        final double s1 = q00 + 2 * p01;
+        final double r2 = DD.twoSumLow(q00, 2 * p01, s1);
+
+        // Collect (s0, s1, r2 + p11 + q01 + q10)
+        final double u = s0 + s1;
+        final double v = DD.fastTwoSumLow(s0, s1, u);
+        return DD.fastTwoSum(u, r2 + p11 + 2 * q01 + v);
+    }
+
+    /**
+     * Compute the division of {@code x} by {@code y}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 1.25-fold slower than {@link DD#divide(DD)}.
+     * Note that division is an order of magnitude slower than multiplication and the
+     * absolute performance difference is significant.
+     *
+     * @param x x.
+     * @param y y.
+     * @return the quotient
+     */
+    public static DD divide(DD x, DD y) {
+        return accurateDivide(x.hi(), x.lo(), y.hi(), y.lo());
+    }
+
+    /**
+     * Compute the division of {@code (x, xx)} by {@code y}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 1.25-fold slower than {@link DD#divide(DD)}.
+     * Note that division is an order of magnitude slower than multiplication and the
+     * absolute performance difference is significant.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the quotient
+     */
+    private static DD accurateDivide(double x, double xx, double y, double yy) {
+        // Long division
+        // quotient q0 = x / y
+        final double q0 = x / y;
+        // remainder r0 = x - q0 * y
+        DD p = accurateMultiply(y, yy, q0);
+        DD r = DD.accurateAdd(x, xx, -p.hi(), -p.lo());
+        // next quotient q1 = r0 / y
+        final double q1 = r.hi() / y;
+        // remainder r1 = r0 - q1 * y
+        p = accurateMultiply(y, yy, q1);
+        r = DD.accurateAdd(r.hi(), r.lo(), -p.hi(), -p.lo());
+        // next quotient q2 = r1 / y
+        final double q2 = r.hi() / y;
+        // Collect (q0, q1, q2)
+        final DD q = DD.fastTwoSum(q0, q1);
+        return DD.twoSum(q.hi(), q.lo() + q2);
+    }
+
+    /**
+     * Compute the inverse of {@code y}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 2-fold slower than {@link DD#reciprocal()}.
+     *
+     * @param y y.
+     * @return the inverse
+     */
+    public static DD reciprocal(DD y) {
+        return accurateReciprocal(y.hi(), y.lo());
+    }
+
+    /**
+     * Compute the inverse of {@code (y, yy)}.
+     * If {@code y = 0} the result is undefined.
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 2-fold slower than {@link DD#reciprocal()}.
+     *
+     * @param y High part of y.
+     * @param yy Low part of y.
+     * @return the inverse
+     */
+    private static DD accurateReciprocal(double y, double yy) {
+        // As per divide using (x, xx) = (1, 0)
+        // quotient q0 = x / y
+        final double q0 = 1 / y;
+        // remainder r0 = x - q0 * y
+        DD p = accurateMultiply(y, yy, q0);
+        // High accuracy add required
+        // This add saves 2 twoSum and 3 fastTwoSum (24 FLOPS) by ignoring the zero low part
+        DD r = DD.accurateAdd(-p.hi(), -p.lo(), 1);
+        // next quotient q1 = r0 / y
+        final double q1 = r.hi() / y;
+        // remainder r1 = r0 - q1 * y
+        p = accurateMultiply(y, yy, q1);
+        // accurateAdd not used as we do not need r1.xx()
+        r = DD.accurateAdd(r.hi(), r.lo(), -p.hi(), -p.lo());
+        // next quotient q2 = r1 / y
+        final double q2 = r.hi() / y;
+        // Collect (q0, q1, q2)
+        final DD q = DD.fastTwoSum(q0, q1);
+        return DD.twoSum(q.hi(), q.lo() + q2);
+    }
+
+    /**
+     * Compute the square root of {@code x}.
+     *
+     * <p>Uses the result {@code Math.sqrt(x)}
+     * if that result is not a finite normalized {@code double}.
+     *
+     * <p>Special cases:
+     * <ul>
+     *  <li>If {@code x} is NaN or less than zero, then the result is {@code (NaN, 0)}.
+     *  <li>If {@code x} is positive infinity, then the result is {@code (+infinity, 0)}.
+     *  <li>If {@code x} is positive zero or negative zero, then the result is {@code (x, 0)}.
+     * </ul>
+     *
+     * <p>The computed result is within 1 eps of the exact result where eps is 2<sup>-106</sup>.
+     *
+     * <p>The performance is approximately 5.5-fold slower than {@link DD#sqrt()}.
+     *
+     * @param x x.
+     * @return {@code sqrt(x)}
+     * @see Math#sqrt(double)
+     * @see Double#MIN_NORMAL
+     */
+    public static DD sqrt(DD x) {
+        // Standard sqrt
+        final DD c = x.sqrt();
+
+        // Here we support {negative, +infinity, nan and zero} edge cases.
+        // (This is the same condition as in DD.sqrt)
+        if (DD.isNotNormal(c.hi())) {
+            return c;
+        }
+
+        // Repeat Dekker's iteration from DD.sqrt with an accurate DD square.
+        // Using an accurate sum for cc does not improve accuracy.
+        final DD u = square(c);
+        final double cc = (x.hi() - u.hi() - u.lo() + x.lo()) * 0.5 / c.hi();
+        return DD.fastTwoSum(c.hi(), c.lo() + cc);
+    }
+
+    /**
+     * Compute the number {@code x} raised to the power {@code n}.
+     *
+     * <p>This uses the powDSimple algorithm of van Mulbregt [1] which applies a Taylor series
+     * adjustment to the result of {@code x^n}:
+     * <pre>
+     * (x+xx)^n = x^n * (1 + xx/x)^n
+     *          = x^n + x^n * (exp(n log(1 + xx/x)) - 1)
+     * </pre>
+     *
+     * <ol>
+     * <li>
+     * van Mulbregt, P. (2018).
+     * <a href="https://doi.org/10.48550/arxiv.1802.06966">Computing the Cumulative Distribution
+     * Function and Quantiles of the One-sided Kolmogorov-Smirnov Statistic</a>
+     * arxiv:1802.06966.
+     * </ol>
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param n Power.
+     * @return the result
+     * @see Math#pow(double, double)
+     */
+    public static DD simplePow(double x, double xx, int n) {
+        // Edge cases. These ignore (x, xx) = (+/-1, 0). The result is Math.pow(x, n).
+        if (n == 0) {
+            return DD.ONE;
+        }
+        // IEEE result for non-finite or zero
+        if (!Double.isFinite(x) || x == 0) {
+            return DD.of(Math.pow(x, n));
+        }
+        // Here the number is non-zero finite
+        if (n < 0) {
+            DD r = computeSimplePow(x, xx, -1L * n);
+            // Safe inversion of small/large values. Reuse the existing multiply scaling factors.
+            // 1 / x = b * 1 / bx
+            if (Math.abs(r.hi()) < SAFE_MULTIPLY_DOWNSCALE) {
+                r = DD.of(r.hi() * SAFE_MULTIPLY, r.lo() * SAFE_MULTIPLY).reciprocal();
+                final double hi = r.hi() * SAFE_MULTIPLY;
+                // Return signed zero by multiplication for infinite
+                final double lo = r.lo() * (Double.isInfinite(hi) ? 0 : SAFE_MULTIPLY);
+                return DD.of(hi, lo);
+            }
+            if (Math.abs(r.hi()) > SAFE_MULTIPLY) {
+                r = DD.of(r.hi() * SAFE_MULTIPLY_DOWNSCALE, r.lo() * SAFE_MULTIPLY_DOWNSCALE).reciprocal();
+                final double hi = r.hi() * SAFE_MULTIPLY_DOWNSCALE;
+                final double lo = r.lo() * SAFE_MULTIPLY_DOWNSCALE;
+                return DD.of(hi, lo);
+            }
+            return r.reciprocal();
+        }
+        return computeSimplePow(x, xx, n);
+    }
+
+    /**
+     * Compute the number {@code x} raised to the power {@code n} (must be strictly positive).
+     *
+     * <p>This method exists to allow negation of the power when it is {@link Integer#MIN_VALUE}
+     * by casting to a long. It is called directly by simplePow and computeSimplePowScaled
+     * when the arguments have already been validated.
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param n Power (must be positive).
+     * @return the result
+     */
+    private static DD computeSimplePow(double x, double xx, long n) {
+        final double y = Math.pow(x, n);
+        final double z = xx / x;
+        // Taylor series: (1 + z)^n = n*z * (1 + ((n-1)*z/2))
+        // Applicable when n*z is small.
+        // Assume xx < epsilon * x.
+        // n > 1e8 => n * xx/x > 1e8 * xx/x == n*z > 1e8 * 1e-16 > 1e-8
+        double w;
+        if (n > LARGE_N) {
+            w = Math.expm1(n * Math.log1p(z));
+        } else {
+            w = n * z * (1 + (n - 1) * z * 0.5);
+        }
+        // w ~ (1+z)^n : z ~ 2^-53
+        // Math.pow(1 + 2 * Math.ulp(1.0), 2^31) ~ 1.0000000000000129
+        // Math.pow(1 - 2 * Math.ulp(1.0), 2^31) ~ 0.9999999999999871
+        // If (x, xx) is normalized a fast-two-sum can be used.
+        // fast-two-sum propagates sign changes for input of (+/-1.0, +/-0.0) (two-sum does not).
+        return DD.fastTwoSum(y, y * w);
+    }
+
+    /**
+     * Compute the number {@code x} raised to the power {@code n}.
+     *
+     * <p>The value is returned as fractional {@code f} and integral
+     * {@code 2^exp} components.
+     * <pre>
+     * (x+xx)^n = (f+ff) * 2^exp
+     * </pre>
+     *
+     * <p>The combined fractional part (f, ff) is in the range {@code [0.5, 1)}.
+     *
+     * <p>Special cases:
+     *
+     * <ul>
+     *  <li>If {@code (x, xx)} is zero the high part of the fractional part is
+     *      computed using {@link Math#pow(double, double) Math.pow(x, n)} and the exponent is 0.
+     *  <li>If {@code n = 0} the fractional part is 0.5 and the exponent is 1.
+     *  <li>If {@code (x, xx)} is an exact power of 2 the fractional part is 0.5 and the exponent
+     *      is the power of 2 minus 1.
+     *  <li>If the result high-part is an exact power of 2 and the low-part has an opposite
+     *      signed non-zero magnitude then the fraction high-part {@code f} will be {@code +/-1} such that
+     *      the double-double number is in the range {@code [0.5, 1)}.
+     *  <li>If the argument is not finite then a fractional representation is not possible.
+     *      In this case the fraction and the scale factor is undefined.
+     * </ul>
+     *
+     * @param x High part of x.
+     * @param xx Low part of x.
+     * @param n Power.
+     * @param exp Power of two scale factor (integral exponent).
+     * @return Fraction part.
+     * @see #simplePow(double, double, int)
+     * @see DD#frexp(int[])
+     */
+    public static DD simplePowScaled(double x, double xx, int n, long[] exp) {
+        // Edge cases.
+        if (n == 0) {
+            exp[0] = 1;
+            return DD.of(0.5);
+        }
+        // IEEE result for non-finite or zero
+        if (!Double.isFinite(x) || x == 0) {
+            exp[0] = 0;
+            return DD.of(Math.pow(x, n));
+        }
+        // Here the number is non-zero finite
+        final int[] ie = {0};
+        DD f = DD.of(x, xx).frexp(ie);
+        final long b = ie[0];
+        if (n < 0) {
+            f = computeSimplePowScaled(b, f.hi(), f.lo(), -1L * n, exp);
+            // Result is a non-zero fraction part so inversion is safe
+            f = f.reciprocal();
+            // Rescale to [0.5, 1.0)
+            f = f.frexp(ie);
+            exp[0] = ie[0] - exp[0];
+            return f;
+        }
+        return computeSimplePowScaled(b, f.hi(), f.lo(), n, exp);
+    }
+
+    /**
+     * Compute the number {@code x} (non-zero finite) raised to the power {@code n} (must be strictly positive).
+     *
+     * <p>This method exists to allow negation of the power when it is {@link Integer#MIN_VALUE}
+     * by casting to a long. By using a fractional representation for the argument
+     * the recursive calls avoid a step to normalise the input.
+     *
+     * @param bx Integral component 2^bx of x.
+     * @param x Fractional high part of x.
+     * @param xx Fractional low part of x.
+     * @param n Power (in [1, 2^31]).
+     * @param exp Power of two scale factor (integral exponent).
+     * @return Fraction part.
+     */
+    private static DD computeSimplePowScaled(long bx, double x, double xx, long n, long[] exp) {
+        // By normalising x we can break apart the power to avoid over/underflow:
+        // x^n = (f * 2^b)^n = 2^bn * f^n
+        long b = bx;
+        double f0 = x;
+        double f1 = xx;
+
+        // Minimise the amount we have to decompose the power. This is done
+        // using either f (<=1) or 2f (>=1) as the fractional representation,
+        // based on which can use a larger exponent without over/underflow.
+        // We approximate the power as 2^b and require a result with the
+        // smallest absolute b. An additional consideration is the low-part ff
+        // which sets a more conservative underflow limit:
+        // f^n              = 2^(-b+53)  => b = -n log2(f) - 53
+        // (2f)^n = 2^n*f^n = 2^b        => b =  n log2(f) + n
+        // Switch-over point for f is at:
+        // -n log2(f) - 53 = n log2(f) + n
+        // 2n log2(f) = -53 - n
+        // f = 2^(-53/2n) * 2^(-1/2)
+        // Avoid a power computation to find the threshold by dropping the first term:
+        // f = 2^(-1/2) = 1/sqrt(2) = sqrt(0.5) = 0.707
+        // This will bias towards choosing f even when (2f)^n would not overflow.
+        // It allows the same safe exponent to be used for both cases.
+
+        // Safe maximum for exponentiation.
+        long m;
+        double af = Math.abs(f0);
+        if (af < ROOT_HALF) {
+            // Choose 2f.
+            // This case will handle (x, xx) = (1, 0) in a single power operation
+            f0 *= 2;
+            f1 *= 2;
+            af *= 2;
+            b -= 1;
+            if (n <= SAFE_EXPONENT_2F || af <= SAFE_2F) {
+                m = n;
+            } else {
+                // f^m < 2^1013
+                // m ~ 1013 / log2(f)
+                m = Math.max(SAFE_EXPONENT_2F, (long) (SAFE_EXPONENT_2F * LN2 / Math.log(af)));
+            }
+        } else {
+            // Choose f
+            if (n <= SAFE_EXPONENT_F || af >= SAFE_F) {
+                m = n;
+            } else {
+                // f^m > 2^-958
+                // m ~ -958 / log2(f)
+                m = Math.max(SAFE_EXPONENT_F, (long) (-SAFE_EXPONENT_F * LN2 / Math.log(af)));
+            }
+        }
+
+        DD f;
+        final int[] expi = {0};
+
+        if (n <= m) {
+            f = computeSimplePow(f0, f1, n);
+            f = f.frexp(expi);
+            exp[0] = b * n + expi[0];
+            return f;
+        }
+
+        // Decompose the power function.
+        // quotient q = n / m
+        // remainder r = n % m
+        // f^n = (f^m)^(n/m) * f^(n%m)
+
+        final long q = n / m;
+        final long r = n % m;
+        // (f^m)
+        // m is safe and > 1
+        f = computeSimplePow(f0, f1, m);
+        f = f.frexp(expi);
+        long qb = expi[0];
+        // (f^m)^(n/m)
+        // q is non-zero but may be 1
+        if (q > 1) {
+            // full simple-pow to ensure safe exponentiation
+            f = computeSimplePowScaled(qb, f.hi(), f.lo(), q, exp);
+            qb = exp[0];
+        }
+        // f^(n%m)
+        // r may be zero or one which do not require another power
+        if (r == 0) {
+            f = f.frexp(expi);
+            exp[0] = b * n + qb + expi[0];
+            return f;
+        }
+        if (r == 1) {
+            f = f.multiply(DD.of(f0, f1));
+            f = f.frexp(expi);
+            exp[0] = b * n + qb + expi[0];
+            return f;
+        }
+        // Here r is safe
+        final DD t = f;
+        f = computeSimplePow(f0, f1, r);
+        f = f.frexp(expi);
+        final long rb = expi[0];
+        // (f^m)^(n/m) * f^(n%m)
+        f = f.multiply(t);
+        // 2^bn * (f^m)^(n/m) * f^(n%m)
+        f = f.frexp(expi);
+        exp[0] = b * n + qb + rb + expi[0];
+        return f;
+    }
+}
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDTest.java
new file mode 100644
index 00000000..97c742cc
--- /dev/null
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/DDTest.java
@@ -0,0 +1,2980 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.core;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.BinaryOperator;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.CsvFileSource;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Test cases for {@link DD} arithmetic.
+ */
+class DDTest {
+    /** Down scale factors to apply to argument y of f(x, y). */
+    private static final double[] DOWN_SCALES = {
+        1.0, 0x1.0p-1, 0x1.0p-2, 0x1.0p-3, 0x1.0p-5, 0x1.0p-10, 0x1.0p-25, 0x1.0p-51, 0x1.0p-52, 0x1.0p-53, 0x1.0p-100
+    };
+    /** Scale factors to apply to argument y of f(x, y). */
+    private static final double[] SCALES = {
+        1.0, 0x1.0p-1, 0x1.0p-2, 0x1.0p-3, 0x1.0p-5, 0x1.0p-10, 0x1.0p-25, 0x1.0p-51, 0x1.0p-52, 0x1.0p-53, 0x1.0p-100,
+        0x1.0p1, 0x1.0p2, 0x1.0p3, 0x1.0p5, 0x1.0p10, 0x1.0p25, 0x1.0p51, 0x1.0p52, 0x1.0p53, 0x1.0p100
+    };
+    /** MathContext for division. A double-double has approximately 34 digits of precision so
+     * use twice this to allow computation of relative error of the results to a useful precision. */
+    private static final MathContext MC_DIVIDE = new MathContext(MathContext.DECIMAL128.getPrecision() * 2);
+    /** A BigDecimal for Long.MAX_VALUE. */
+    private static final BigDecimal BD_LONG_MAX = BigDecimal.valueOf(Long.MAX_VALUE);
+    /** A BigDecimal for Long.MIN_VALUE. */
+    private static final BigDecimal BD_LONG_MIN = BigDecimal.valueOf(Long.MIN_VALUE);
+    /** Number of random samples for arithmetic data. */
+    private static final int SAMPLES = 100;
+    /** The epsilon for relative error. Equivalent to 2^-106 for the precision of a double-double
+     * 106-bit mantissa. This value is used to report the accuracy of the functions in the DD javadoc. */
+    private static final double EPS = 0x1.0p-106;
+
+    @Test
+    void testOne() {
+        Assertions.assertEquals(1, DD.ONE.hi());
+        Assertions.assertEquals(0, DD.ONE.lo());
+        Assertions.assertSame(DD.ONE, DD.of(1.23).one());
+    }
+
+    @Test
+    void testZero() {
+        Assertions.assertEquals(0, DD.ZERO.hi());
+        Assertions.assertEquals(0, DD.ZERO.lo());
+        Assertions.assertSame(DD.ZERO, DD.of(1.23).zero());
+    }
+
+    @ParameterizedTest
+    @ValueSource(doubles = {0, 1, Math.PI, Double.MIN_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN})
+    void testOfDouble(double x) {
+        DD dd = DD.of(x);
+        Assertions.assertEquals(x, dd.hi(), "x hi");
+        Assertions.assertEquals(0, dd.lo(), "x lo");
+        dd = DD.of(-x);
+        Assertions.assertEquals(-x, dd.hi(), "-x hi");
+        Assertions.assertEquals(0, dd.lo(), "-x lo");
+    }
+
+    @ParameterizedTest
+    @ValueSource(ints = {0, 1, 42, 4674567, Integer.MIN_VALUE, Integer.MAX_VALUE - 42, Integer.MAX_VALUE})
+    void testOfInt(int x) {
+        DD dd = DD.of(x);
+        Assertions.assertEquals(x, dd.hi(), "x hi");
+        Assertions.assertEquals(0, dd.lo(), "x lo");
+        dd = DD.of(-x);
+        Assertions.assertEquals(-x, dd.hi(), "-x hi");
+        Assertions.assertEquals(0, dd.lo(), "-x lo");
+    }
+
+    /**
+     * Test conversion of a {@code long}. The upper part should be the value cast as a double.
+     * The lower part is any remaining value. If done incorrectly this can lose bits due
+     * to rounding to 2^53 so we have extra cases for this.
+     */
+    @ParameterizedTest
+    @ValueSource(longs = {0, 1, 42, 89545664, 8263492364L, Long.MIN_VALUE,
+        Long.MAX_VALUE - (1L << 10), Long.MAX_VALUE - 42, Long.MAX_VALUE - 1, Long.MAX_VALUE})
+    void testOfLong(long x) {
+        DD dd = DD.of(x);
+        Assertions.assertEquals(x, dd.hi(), "x hi should be (double) x");
+        Assertions.assertEquals(BigDecimal.valueOf(x).subtract(bd(x)).doubleValue(), dd.lo(), "x lo should be remaining bits");
+        dd = DD.of(-x);
+        Assertions.assertEquals(-x, dd.hi(), "-x hi should be (double) -x");
+        Assertions.assertEquals(BigDecimal.valueOf(-x).subtract(bd(-x)).doubleValue(), dd.lo(), "-x lo should be remaining bits");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds"})
+    void testFromBigDecimal(double x, double y) {
+        final BigDecimal xy = bd(x).add(bd(y));
+        final DD z = DD.from(xy);
+        Assertions.assertEquals(xy.doubleValue(), z.hi(), "hi should be the double representation");
+        Assertions.assertEquals(xy.subtract(bd(z.hi())).doubleValue(), z.lo(), "lo should be the double representation of the round-off");
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        "1e500, Infinity, 0",
+        "-1e600, -Infinity, 0",
+    })
+    void testFromBigDecimalInfinite(String value, double x, double xx) {
+        final DD z = DD.from(new BigDecimal(value));
+        Assertions.assertEquals(x, z.hi(), "hi");
+        Assertions.assertEquals(xx, z.lo(), "lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testIsFinite(double x, double xx) {
+        final DD dd = DD.of(x, xx);
+        final boolean isFinite = Double.isFinite(x + xx);
+        Assertions.assertEquals(isFinite, dd.isFinite(), "finite evaluated sum");
+    }
+
+    static Stream<Arguments> testIsFinite() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        // Note: (max + max) will overflow but the DD number should be finite.
+        final double[] values = {0, 1, Double.MAX_VALUE, Double.MIN_VALUE,
+            Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN};
+        for (final double x : values) {
+            for (final double xx : values) {
+                builder.add(Arguments.of(x, xx));
+            }
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds", "testDoubleFloatValue"})
+    void testDoubleFloatValue(double x, double y) {
+        // By creating a non-normalized DD this tests the two parts are added
+        final DD dd = DD.of(x, y);
+        final double sum = x + y;
+        Assertions.assertEquals(sum, dd.doubleValue());
+        Assertions.assertEquals((float) sum, dd.floatValue());
+    }
+
+    static Stream<Arguments> testDoubleFloatValue() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        // Note: (max + max) will overflow but the DD number should be finite.
+        final double[] values = {0, 1, -42, -0.5, Double.MAX_VALUE, Double.MIN_VALUE,
+            -Double.MIN_NORMAL, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN};
+        for (final double x : values) {
+            for (final double xx : values) {
+                builder.add(Arguments.of(x, xx));
+            }
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds", "testDoubleFloatValue", "testLongIntValue"})
+    void testLongIntValue(double x, double y) {
+        // Number must be normalized
+        final DD dd = DD.ofSum(x, y);
+        if (Double.isFinite(x) && Double.isFinite(y)) {
+            final BigDecimal bd = bd(x).add(bd(y));
+            // The long/int value should act as if a truncating cast towards zero
+            long longExpected;
+            if (bd.compareTo(BD_LONG_MIN) <= 0) {
+                longExpected = Long.MIN_VALUE;
+            } else if (bd.compareTo(BD_LONG_MAX) >= 0) {
+                longExpected = Long.MAX_VALUE;
+            } else {
+                longExpected = bd.longValue();
+            }
+            Assertions.assertEquals(longExpected, dd.longValue(), "finite parts long");
+            final int intExpected = (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, longExpected));
+            Assertions.assertEquals(intExpected, dd.intValue(), "finite parts int");
+        } else {
+            // IEEE754 result for casting a non-finite double
+            Assertions.assertEquals((long) (x + y), dd.longValue(), "non-finite parts long");
+            Assertions.assertEquals((int) (x + y), dd.intValue(), "non-finite parts int");
+        }
+    }
+
+    static Stream<Arguments> testLongIntValue() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final double[] values = {0, 1, 42,
+            Integer.MIN_VALUE, Integer.MAX_VALUE,
+            1L << 52, 1L << 53, 1L << 54,
+            Long.MIN_VALUE, Long.MAX_VALUE};
+        for (final double x : values) {
+            for (final double xx : values) {
+                if (Math.abs(xx) < Math.abs(x)) {
+                    builder.add(Arguments.of(x, xx));
+                    builder.add(Arguments.of(x, -xx));
+                    builder.add(Arguments.of(-x, xx));
+                    builder.add(Arguments.of(-x, -xx));
+                }
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Test the value is exact after a sum of longs.
+     * This exercises {@link DD#of(long)}, {@link DD#bigDecimalValue()} and {@link DD#longValue()}.
+     * With a 106-bit mantissa a DD should be able to exactly sum up to 2^43 longs.
+     */
+    @ParameterizedTest
+    @MethodSource
+    void testLongSum(long[] values) {
+        DD dd = DD.ZERO;
+        BigDecimal bd = BigDecimal.ZERO;
+        for (final long x : values) {
+            dd = dd.add(DD.of(x));
+            bd = bd.add(BigDecimal.valueOf(x));
+        }
+        Assertions.assertEquals(0, bd.compareTo(dd.bigDecimalValue()), "BigDecimal value");
+        // The long value should act as if a truncating cast towards zero
+        long expected;
+        if (bd.compareTo(BD_LONG_MIN) <= 0) {
+            expected = Long.MIN_VALUE;
+        } else if (bd.compareTo(BD_LONG_MAX) >= 0) {
+            expected = Long.MAX_VALUE;
+        } else {
+            expected = bd.longValue();
+        }
+        Assertions.assertEquals(expected, dd.longValue(), "long value");
+    }
+
+    static Stream<long[]> testLongSum() {
+        final Stream.Builder<long[]> builder = Stream.builder();
+        // Edge cases around min/max long value, including overflow a long then recover
+        final long min = Long.MIN_VALUE;
+        final long max = Long.MAX_VALUE;
+        builder.add(new long[] {min, 1});
+        builder.add(new long[] {min, -1, 1});
+        builder.add(new long[] {min, -33, 67});
+        builder.add(new long[] {min, min, max, 67});
+        builder.add(new long[] {max});
+        builder.add(new long[] {max, -1});
+        builder.add(new long[] {max, 1, -1});
+        builder.add(new long[] {max, 13, -14});
+        builder.add(new long[] {max, max, min});
+        // Random
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 1; i < 20; i++) {
+            // The average sum will be approximately zero, actual cases may overflow.
+            builder.add(rng.longs(2 * i).toArray());
+            // Here we make the chance of overflow smaller by dropping 4 bits.
+            // The long is still greater than the 53-bit precision of a double.
+            builder.add(rng.longs(2 * i).map(x -> x >> 4).toArray());
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds", "testDoubleFloatValue", "testLongIntValue"})
+    void testBigDecimalValue(double x, double y) {
+        // By creating a non-normalized DD this tests the two parts are added
+        final DD dd = DD.of(x, y);
+        if (Double.isFinite(x) && Double.isFinite(y)) {
+            Assertions.assertEquals(0, bd(x).add(bd(y)).compareTo(dd.bigDecimalValue()));
+            // Note: no test for isFinite==true as a non-normalized number can be infinite
+            // if the sum of the parts overflows (isFinite is conditioned on the evaluated sum)
+        } else {
+            // Cannot create a BigDecimal if not finite
+            Assertions.assertThrows(NumberFormatException.class, () -> dd.bigDecimalValue());
+            Assertions.assertFalse(dd.isFinite(), "This should be non-finite");
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds"})
+    void testFastTwoSum(double xa, double ya) {
+        // |x| > |y|
+        double x;
+        double y;
+        if (Math.abs(xa) < Math.abs(ya)) {
+            x = ya;
+            y = xa;
+        } else {
+            x = xa;
+            y = ya;
+        }
+        DD z;
+        final BigDecimal bx = bd(x);
+        final Supplier<String> msg = () -> String.format("%s+%s", x, y);
+        for (final double scale : DOWN_SCALES) {
+            final double sy = scale * y;
+            z = DD.fastTwoSum(x, sy);
+            final double hi = x + sy;
+            final double lo = bx.add(bd(sy)).subtract(bd(hi)).doubleValue();
+            // fast-two-sum should be exact
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " lo: scale=" + scale);
+            Assertions.assertEquals(hi, hi + lo, () -> msg.get() + " hi+lo: scale=" + scale);
+            Assertions.assertEquals(hi, z.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+            // Same as fastTwoDiff
+            z = DD.fastTwoDiff(x, -sy);
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " fastTwoDiff hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " fastTwoDiff lo: scale=" + scale);
+            z = DD.fastTwoSum(x, -sy);
+            final double z0 = z.hi();
+            final double z1 = z.lo();
+            z = DD.fastTwoDiff(x, sy);
+            Assertions.assertEquals(z0, z.hi(), () -> msg.get() + " fastTwoSum hi: scale=" + scale);
+            Assertions.assertEquals(z1, z.lo(), () -> msg.get() + " fastTwoSum lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testDoubleFloatValue", "testLongIntValue"})
+    void testNegate(double x, double y) {
+        // By creating a non-normalized DD this tests the two parts are negated
+        final DD dd = DD.of(x, y).negate();
+        Assertions.assertEquals(-x, dd.hi());
+        Assertions.assertEquals(-y, dd.lo());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testDoubleFloatValue", "testLongIntValue"})
+    void testAbs(double x, double y) {
+        // By creating a non-normalized DD this tests the parts are returned based only
+        // on the high part x
+        final DD dd = DD.of(x, y).abs();
+        if (x < 0) {
+            Assertions.assertEquals(-x, dd.hi());
+            Assertions.assertEquals(-y, dd.lo());
+        } else if (x == 0) {
+            // Canonical absolute of zero, y is ignored
+            Assertions.assertEquals(0.0, dd.hi());
+            Assertions.assertEquals(0.0, dd.lo());
+        } else {
+            Assertions.assertEquals(x, dd.hi());
+            Assertions.assertEquals(y, dd.lo());
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"twoSumAddeds"})
+    void testTwoSum(double x, double y) {
+        // This method currently uses DD.add and DD.subtract and not the internally named
+        // twoSum and twoDiff.
+        DD z;
+        final BigDecimal bx = bd(x);
+        final Supplier<String> msg = () -> String.format("%s+%s", x, y);
+        for (final double scale : DOWN_SCALES) {
+            final double sy = scale * y;
+            z = DD.ofSum(x, sy);
+            final double hi = x + sy;
+            final double lo = bx.add(bd(sy)).subtract(bd(hi)).doubleValue();
+            // two-sum should be exact
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " lo: scale=" + scale);
+            Assertions.assertEquals(hi, hi + lo, () -> msg.get() + " hi+lo: scale=" + scale);
+            Assertions.assertEquals(hi, z.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+            z = DD.ofSum(sy, x);
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " reversed hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " reversed lo: scale=" + scale);
+            // Same as twoDiff
+            z = DD.ofDifference(x, -sy);
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " twoDiff hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " twoDiff lo: scale=" + scale);
+            z = DD.ofSum(x, -sy);
+            final double z0 = z.hi();
+            final double z1 = z.lo();
+            z = DD.ofDifference(x, sy);
+            Assertions.assertEquals(z0, z.hi(), () -> msg.get() + " twoSum hi: scale=" + scale);
+            Assertions.assertEquals(z1, z.lo(), () -> msg.get() + " twoSum lo: scale=" + scale);
+        }
+    }
+
+    static Stream<Arguments> twoSumAddeds() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < SAMPLES; i++) {
+            builder.add(Arguments.of(signedNormalDouble(rng), signedNormalDouble(rng)));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Test {@link DD#twoSum(double, double)} with cases that have non-normal input
+     * or create overflow.
+     */
+    @Test
+    void testTwoSumSpecialCases() {
+        // x + y is sub-normal or zero
+        assertSum(0.0, Math.nextDown(Double.MIN_NORMAL), -Math.ulp(Double.MIN_NORMAL));
+        assertSum(0.0, -1.0, 1.0);
+        // x or y is infinite or NaN
+        assertSum(Double.NaN, 1.0, Double.POSITIVE_INFINITY);
+        assertSum(Double.NaN, 1.0, Double.NEGATIVE_INFINITY);
+        assertSum(Double.NaN, 1.0, Double.NaN);
+        // x + y is infinite
+        assertSum(Double.NaN, 0x1.0p1023, 0x1.0p1023);
+        assertSum(Double.NaN, Math.ulp(Double.MAX_VALUE), Double.MAX_VALUE);
+    }
+
+    private static void assertSum(double expectedLo, double x, double y) {
+        final DD z = DD.ofSum(x, y);
+        Assertions.assertEquals(x + y, z.hi(), "hi");
+        // Requires a delta of 0.0 to assert -0.0 == 0.0
+        Assertions.assertEquals(expectedLo, z.lo(), 0.0, "lo");
+        final DD z2 = DD.ofSum(y, x);
+        Assertions.assertEquals(z.hi(), z2.hi(), "y+x hi");
+        Assertions.assertEquals(z.lo(), z2.lo(), "y+x lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testTwoProd(double x, double y) {
+        DD z;
+        final BigDecimal bx = bd(x);
+        final Supplier<String> msg = () -> String.format("%s*%s", x, y);
+        for (final double scale : DOWN_SCALES) {
+            final double sy = scale * y;
+            z = DD.ofProduct(x, sy);
+            final double hi = x * sy;
+            final double lo = bx.multiply(bd(sy)).subtract(bd(hi)).doubleValue();
+            // two-prod should be exact
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " lo: scale=" + scale);
+            Assertions.assertEquals(hi, hi + lo, () -> msg.get() + " hi+lo: scale=" + scale);
+            Assertions.assertEquals(hi, z.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+            z = DD.ofProduct(sy, x);
+            Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " reversed hi: scale=" + scale);
+            Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " reversed lo: scale=" + scale);
+        }
+    }
+
+    static Stream<Arguments> testTwoProd() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < SAMPLES; i++) {
+            builder.add(Arguments.of(signedNormalDouble(rng), signedNormalDouble(rng)));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Test {@link DD#twoProd(double, double)} with cases that have non-normal input
+     * or create intermediate overflow when splitting.
+     */
+    @Test
+    void testTwoProdSpecialCases() {
+        // x * y is sub-normal or zero
+        assertProduct(0.0, 1.0, Math.nextDown(Double.MIN_NORMAL));
+        assertProduct(0.0, -1.0, Math.nextDown(Double.MIN_NORMAL));
+        assertProduct(0.0, 0x1.0p-512, 0x1.0p-512);
+        // x or y is infinite or NaN
+        assertProduct(Double.NaN, 1.0, Double.POSITIVE_INFINITY);
+        assertProduct(Double.NaN, 1.0, Double.NEGATIVE_INFINITY);
+        assertProduct(Double.NaN, 1.0, Double.NaN);
+        // x * y is infinite
+        assertProduct(Double.NaN, 0x1.0p511, 0x1.0p513);
+        // |x| or |y| > ~2^997
+        assertProduct(Double.NaN, 0.5, 0x1.0p997);
+        assertProduct(Double.NaN, 0.5, Double.MAX_VALUE);
+    }
+
+    private static void assertProduct(double expectedLo, double x, double y) {
+        final DD z = DD.ofProduct(x, y);
+        Assertions.assertEquals(x * y, z.hi(), "hi");
+        // Requires a delta of 0.0 to assert -0.0 == 0.0
+        Assertions.assertEquals(expectedLo, z.lo(), 0.0, "lo");
+        final DD z2 = DD.ofProduct(y, x);
+        Assertions.assertEquals(z.hi(), z2.hi(), "y*x hi");
+        Assertions.assertEquals(z.lo(), z2.lo(), "y*x lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testTwoProd"})
+    void testTwoSquare(double x) {
+        DD z;
+        final BigDecimal bx = bd(x);
+        final Supplier<String> msg = () -> String.format("%s^2", x);
+        z = DD.ofSquare(x);
+        final double hi = x * x;
+        final double lo = bx.pow(2).subtract(bd(hi)).doubleValue();
+        // two-square should be exact
+        Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " hi");
+        Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " lo");
+        Assertions.assertEquals(hi, hi + lo, () -> msg.get() + " hi+lo");
+        Assertions.assertEquals(hi, z.doubleValue(), () -> msg.get() + " doubleValue");
+        z = DD.ofSquare(-x);
+        Assertions.assertEquals(hi, z.hi(), () -> msg.get() + " (-x)^2 hi");
+        Assertions.assertEquals(lo, z.lo(), () -> msg.get() + " (-x)^2 lo");
+    }
+
+    /**
+     * Test {@link DD#twoSquare(double)} with cases that have non-normal input
+     * or create intermediate overflow when splitting.
+     */
+    @Test
+    void testTwoSquareSpecialCases() {
+        // x * x is sub-normal or zero
+        assertSquare(0.0, 0);
+        assertSquare(0.0, Double.MIN_NORMAL);
+        assertSquare(0.0, 0x1.0p-512);
+        // x is infinite or NaN
+        assertSquare(Double.NaN, Double.POSITIVE_INFINITY);
+        assertSquare(Double.NaN, Double.NaN);
+        // x * x is infinite
+        assertSquare(Double.NaN, 0x1.0p512);
+        // |x| > ~2^997
+        assertSquare(Double.NaN, 0x1.0p997);
+        assertSquare(Double.NaN, Double.MAX_VALUE);
+    }
+
+    private static void assertSquare(double expectedLo, double x) {
+        final DD z = DD.ofSquare(x);
+        Assertions.assertEquals(x * x, z.hi(), "hi");
+        // Requires a delta of 0.0 to assert -0.0 == 0.0
+        Assertions.assertEquals(expectedLo, z.lo(), 0.0, "lo");
+        final DD z2 = DD.ofSquare(-x);
+        Assertions.assertEquals(z.hi(), z2.hi(), "(-x)^2 hi");
+        Assertions.assertEquals(z.lo(), z2.lo(), "(-x)^2 lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testQuotient(double x, double y) {
+        DD s;
+        final Supplier<String> msg = () -> String.format("%s/%s", x, y);
+
+        s = DD.fromQuotient(x, y);
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).divide(bd(y), MC_DIVIDE);
+        // double-double has 106-bits precision.
+        // This passes with a relative error of 2^-107.
+        TestUtils.assertEquals(e, s, 0.5 * EPS, () -> msg.get());
+
+        // Same as if low-part of x and y is zero
+        s = DD.of(x).divide(DD.of(y));
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " xx=yy=0 hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " xx=yy=0 lo");
+    }
+
+    static Stream<Arguments> testQuotient() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            builder.add(Arguments.of(signedNormalDouble(rng), signedNormalDouble(rng)));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Test {@link DD#fromQuotient(double, double)} with cases that have non-normal input
+     * or create intermediate overflow when splitting.
+     */
+    @Test
+    void testQuotientSpecialCases() {
+        // x / y is sub-normal or zero
+        assertQuotient(0.0, Double.MIN_NORMAL, 3);
+        assertQuotient(0.0, Math.nextUp(Double.MIN_NORMAL), 2);
+        assertQuotient(0.0, 0, 1);
+        // x is infinite or NaN
+        assertQuotient(Double.NaN, Double.POSITIVE_INFINITY, 1);
+        assertQuotient(Double.NaN, Double.NEGATIVE_INFINITY, 1);
+        assertQuotient(Double.NaN, Double.NaN, 1);
+        // y is infinite (here low part could be zero if checks were added)
+        assertQuotient(Double.NaN, 1.0, Double.POSITIVE_INFINITY);
+        assertQuotient(Double.NaN, 1.0, Double.NEGATIVE_INFINITY);
+        // y is nan
+        assertQuotient(Double.NaN, 1.0, Double.NaN);
+        // x / y is infinite
+        assertQuotient(Double.NaN, 0x1.0p511, 0x1.0p-513);
+        assertQuotient(Double.NaN, 1, Double.MIN_VALUE);
+        // |x / y| > ~2^997
+        assertQuotient(Double.NaN, 0x1.0p997, 0.5);
+        assertQuotient(Double.NaN, Double.MAX_VALUE, 2);
+        // |y| > ~2^997
+        assertQuotient(Double.NaN, 0.5, 0x1.0p997);
+        assertQuotient(Double.NaN, 2, Double.MAX_VALUE);
+        // x / y is sub-normal or zero with intermediate overflow
+        assertQuotient(Double.NaN, 0.5, Double.MAX_VALUE);
+        assertQuotient(Double.NaN, Double.MIN_NORMAL, 0x1.0p997);
+    }
+
+    private static void assertQuotient(double expectedLo, double x, double y) {
+        final DD z = DD.fromQuotient(x, y);
+        Assertions.assertEquals(x / y, z.hi(), "hi");
+        // Requires a delta of 0.0 to assert -0.0 == 0.0
+        Assertions.assertEquals(expectedLo, z.lo(), 0.0, "lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDouble"})
+    void testAddDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final BigDecimal bx = bd(x).add(bd(xx));
+        final Supplier<String> msg = () -> String.format("(%s,%s)+%s", x, xx, y);
+        for (final double scale : SCALES) {
+            final double sy = scale * y;
+            s = dd.add(sy);
+            // Check normalized
+            final double hi = s.hi();
+            final double lo = s.lo();
+            Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+
+            final BigDecimal e = bx.add(bd(sy));
+            // double-double addition should be within 4 eps^2 with eps = 2^-53.
+            // A single addition is 2 eps^2.
+            TestUtils.assertEquals(e, s, 2.0 * EPS, () -> msg.get() + " scale=" + scale);
+
+            // Same as if low-part of y is zero
+            s = dd.add(DD.of(sy));
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " yy=0 hi: scale=" + scale);
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " yy=0 lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDouble"})
+    void testAccurateAddDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final BigDecimal bx = bd(x).add(bd(xx));
+        final Supplier<String> msg = () -> String.format("(%s,%s)+%s", x, xx, y);
+        for (final double scale : SCALES) {
+            final double sy = scale * y;
+            s = DDExt.add(dd, sy);
+            // Check normalized
+            final double hi = s.hi();
+            final double lo = s.lo();
+            Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+
+            final BigDecimal e = bx.add(bd(sy));
+            // double-double addition should be within 4 eps^2 with eps = 2^-53
+            // Passes at 1 eps^2.
+            // Note:
+            // It may be sporadically failed by add as data is random. The test could be updated
+            // to assert the RMS relative error of accurateAdd is lower than add.
+            TestUtils.assertEquals(e, s, EPS, () -> msg.get() + " scale=" + scale);
+
+            // Additional checks for accurateAdd:
+            // (Note: These are failed by add for cases of large cancellation, or
+            // non-overlapping addends. For reasonable cases the lo-part is within 4 ulp.)
+            // e = full expansion series of m numbers, low suffix is smallest
+            // |e - e_m| <= ulp(e_m) -> hi is a 1 ULP approximation to the IEEE double result
+            TestUtils.assertEquals(e.doubleValue(), hi, 1, () -> msg.get() + " hi: scale=" + scale);
+            // |sum_i^{m-1} (e_i)| <= ulp(e - e_m)
+            final double esem = e.subtract(bd(hi)).doubleValue();
+            TestUtils.assertEquals(esem, lo, 1, () -> msg.get() + " lo: scale=" + scale);
+
+            // Same as if low-part of y is zero
+            s = DDExt.add(dd, DD.of(sy));
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " yy=0 hi: scale=" + scale);
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " yy=0 lo: scale=" + scale);
+        }
+    }
+
+    static Stream<Arguments> addDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), signedNormalDouble(rng)));
+        }
+        // Cases of large cancellation
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final double x = s.hi();
+            final double xx = s.lo();
+            final int dir = rng.nextBoolean() ? 1 : -1;
+            final double ulp = Math.ulp(x);
+            builder.add(Arguments.of(x, xx, -x));
+            builder.add(Arguments.of(x, xx, -(x + dir * ulp)));
+            builder.add(Arguments.of(x, xx, -(x + dir * ulp * 2)));
+            builder.add(Arguments.of(x, xx, -(x + dir * ulp * 3)));
+        }
+        // Cases requiring correct rounding of low
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final double x = s.hi();
+            final double xx = s.lo();
+            final double hUlpXX = Math.ulp(xx) / 2;
+            final double hUlpXXu = Math.nextUp(hUlpXX);
+            final double hUlpXXd = Math.nextDown(hUlpXX);
+            builder.add(Arguments.of(x, xx, hUlpXX));
+            builder.add(Arguments.of(x, xx, -hUlpXX));
+            builder.add(Arguments.of(x, xx, hUlpXXu));
+            builder.add(Arguments.of(x, xx, -hUlpXXu));
+            builder.add(Arguments.of(x, xx, hUlpXXd));
+            builder.add(Arguments.of(x, xx, -hUlpXXd));
+        }
+        // Create a summation of non-overlapping parts
+        for (int i = 0; i < 10; i++) {
+            final double x = signedNormalDouble(rng);
+            final double xx = Math.ulp(x) / 2;
+            final double y = Math.ulp(xx) / 2;
+            final double y1 = Math.nextUp(y);
+            final double y2 = Math.nextDown(y);
+            s = DD.twoSum(x, xx);
+            builder.add(Arguments.of(s.hi(), s.lo(), y));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y));
+            builder.add(Arguments.of(s.hi(), s.lo(), y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), y2));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y2));
+            s = DD.twoSum(x, Math.nextUp(xx));
+            builder.add(Arguments.of(s.hi(), s.lo(), y));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y));
+            builder.add(Arguments.of(s.hi(), s.lo(), y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), y2));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y2));
+            s = DD.twoSum(x, Math.nextDown(xx));
+            builder.add(Arguments.of(s.hi(), s.lo(), y));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y));
+            builder.add(Arguments.of(s.hi(), s.lo(), y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y1));
+            builder.add(Arguments.of(s.hi(), s.lo(), y2));
+            builder.add(Arguments.of(s.hi(), s.lo(), -y2));
+        }
+
+        // Fails add at 1.35 * eps
+        builder.add(Arguments.of(1.2175415583363745, -9.201751961322592E-17, -1.2175415583363745));
+        // Fails add at 1.61 * eps
+        builder.add(Arguments.of(1.0559874267393727, 7.55723980093395E-17, -1.7469209528242222));
+        // Fails add at 1.59 * eps
+        builder.add(Arguments.of(1.1187969556288173, -5.666077383672716E-17, -1.9617226734248885));
+
+        // Taken from addDoubleDouble.
+        // Cases that fail exact summation to a single double if performed incorrectly.
+        // These require correct propagation of the round-off to the high-part.
+        // The double-double high-part may not be exact but summed with the low-part
+        // it should be <= 0.5 ulp of the IEEE double result.
+        builder.add(Arguments.of(-1.8903599998005227, 1.2825149462328469E-17, 1.8903599998005232, 1.2807862928011876E-17));
+        builder.add(Arguments.of(1.8709715815417154, 2.542250988259237E-17, -1.8709715815417152, 1.982876215341407E-17));
+        builder.add(Arguments.of(-1.8246677074340567, 2.158144877411339E-17, 1.8246677074340565, 2.043199561107511E-17));
+
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDoubleDouble"})
+    void testAddDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final BigDecimal bx = bd(x).add(bd(xx));
+        final Supplier<String> msg = () -> String.format("(%s,%s)+(%s,%s)", x, xx, y, yy);
+        for (final double scale : SCALES) {
+            final DD sy = DD.of(scale * y, scale * yy);
+            s = dd.add(sy);
+            // Check normalized
+            final double hi = s.hi();
+            final double lo = s.lo();
+            Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+
+            final BigDecimal e = bx.add(bd(scale * y)).add(bd(scale * yy));
+            // double-double addition should be within 4 eps^2 with eps = 2^-53.
+            // Passes at 3 eps^2.
+            TestUtils.assertEquals(e, s, 3 * EPS, () -> msg.get() + " scale=" + scale);
+
+            // Same if reversed
+            s = sy.add(dd);
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + "reversed hi: scale=" + scale);
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + "reversed lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDoubleDouble"})
+    void testAccurateAddDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final BigDecimal bx = bd(x).add(bd(xx));
+        final Supplier<String> msg = () -> String.format("(%s,%s)+(%s,%s)", x, xx, y, yy);
+        for (final double scale : SCALES) {
+            final DD sy = DD.of(scale * y, scale * yy);
+            s = DDExt.add(dd, sy);
+            // Check normalized
+            final double hi = s.hi();
+            final double lo = s.lo();
+            Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue: scale=" + scale);
+
+            final BigDecimal e = bx.add(bd(scale * y)).add(bd(scale * yy));
+            // double-double addition should be within 4 eps^2 with eps = 2^-53.
+            // Note that the extra computation in add vs accurateAdd
+            // lowers the tolerance to eps^2. This tolerance is consistently failed by add.
+            TestUtils.assertEquals(e, s, EPS, () -> msg.get() + " scale=" + scale);
+
+            // Additional checks for accurateAdd.
+            // (Note: These are failed by add for cases of large cancellation, or
+            // non-overlapping addends. For reasonable cases the lo-part is within 4 ulp.
+            // Thus add creates a double-double that is a better estimate of the first two
+            // terms of the full expansion of e.)
+            // e = full expansion series of m numbers, low suffix is smallest
+            // |e - e_m| <= ulp(e_m) -> hi is a 1 ULP approximation to the IEEE double result
+            TestUtils.assertEquals(e.doubleValue(), hi, 1, () -> msg.get() + " hi: scale=" + scale);
+            // |sum_i^{m-1} (e_i)| <= ulp(e - e_m)
+            final double esem = e.subtract(bd(hi)).doubleValue();
+            TestUtils.assertEquals(esem, lo, 1, () -> msg.get() + " lo: scale=" + scale);
+
+            // Same if reversed
+            s = DDExt.add(sy, dd);
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + "reversed hi: scale=" + scale);
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + "reversed lo: scale=" + scale);
+        }
+    }
+
+    static Stream<Arguments> addDoubleDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final DD t = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), t.hi(), t.lo()));
+        }
+        // Cases of large cancellation
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final double x = s.hi();
+            final double xx = s.lo();
+            final int dir = rng.nextBoolean() ? 1 : -1;
+            final double ulp = Math.ulp(x);
+            double yy = signedNormalDouble(rng);
+            yy = Math.scalb(yy, Math.getExponent(xx));
+            add(builder, s, -x, -xx);
+            add(builder, s, -x, -(xx + dir * ulp));
+            add(builder, s, -x, -(xx + dir * ulp * 2));
+            add(builder, s, -x, -(xx + dir * ulp * 3));
+            add(builder, s, -x, yy);
+            add(builder, s, -x, yy + ulp);
+            add(builder, s, -(x + dir * ulp), -xx);
+            add(builder, s, -(x + dir * ulp), -(xx + dir * ulp));
+            add(builder, s, -(x + dir * ulp), -(xx + dir * ulp * 2));
+            add(builder, s, -(x + dir * ulp), -(xx + dir * ulp * 3));
+            add(builder, s, -(x + dir * ulp), yy);
+            add(builder, s, -(x + dir * ulp), yy + ulp);
+        }
+        // Cases requiring correct rounding of low
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final double x = s.hi();
+            final double xx = s.lo();
+            final int dir = rng.nextBoolean() ? 1 : -1;
+            final double ulpX = Math.ulp(x);
+            final double hUlpXX = Math.ulp(xx) / 2;
+            final double hUlpXXu = Math.nextUp(hUlpXX);
+            final double hUlpXXd = Math.nextDown(hUlpXX);
+            add(builder, s, -x,  hUlpXX);
+            add(builder, s, -x, -hUlpXX);
+            add(builder, s, -x, hUlpXXu);
+            add(builder, s, -x, -hUlpXXu);
+            add(builder, s, -x, hUlpXXd);
+            add(builder, s, -x, -hUlpXXd);
+            add(builder, s, -(x + dir * ulpX),  hUlpXX);
+            add(builder, s, -(x + dir * ulpX), -hUlpXX);
+            add(builder, s, -(x + dir * ulpX), hUlpXXu);
+            add(builder, s, -(x + dir * ulpX), -hUlpXXu);
+            add(builder, s, -(x + dir * ulpX), hUlpXXd);
+            add(builder, s, -(x + dir * ulpX), -hUlpXXd);
+        }
+        // Create a summation of non-overlapping parts
+        for (int i = 0; i < 10; i++) {
+            final double x = signedNormalDouble(rng);
+            final double xx = Math.ulp(x) / 2;
+            final double y = Math.ulp(xx) / 2;
+            final double yy = Math.ulp(y) / 2;
+            final double yy1 = Math.nextUp(yy);
+            final double yy2 = Math.nextDown(yy);
+            s = DD.twoSum(x, xx);
+            add(builder, s, y, yy);
+            add(builder, s, y, -yy);
+            add(builder, s, y, yy1);
+            add(builder, s, y, -yy1);
+            add(builder, s, y, yy2);
+            add(builder, s, y, -yy2);
+            add(builder, s, -y, yy);
+            add(builder, s, -y, -yy);
+            add(builder, s, -y, yy1);
+            add(builder, s, -y, -yy1);
+            add(builder, s, -y, yy2);
+            add(builder, s, -y, -yy2);
+            s = DD.twoSum(x, Math.nextUp(xx));
+            add(builder, s, y, yy);
+            add(builder, s, y, -yy);
+            add(builder, s, y, yy1);
+            add(builder, s, y, -yy1);
+            add(builder, s, y, yy2);
+            add(builder, s, y, -yy2);
+            add(builder, s, -y, yy);
+            add(builder, s, -y, -yy);
+            add(builder, s, -y, yy1);
+            add(builder, s, -y, -yy1);
+            add(builder, s, -y, yy2);
+            add(builder, s, -y, -yy2);
+            s = DD.twoSum(x, Math.nextDown(xx));
+            add(builder, s, y, yy);
+            add(builder, s, y, -yy);
+            add(builder, s, y, yy1);
+            add(builder, s, y, -yy1);
+            add(builder, s, y, yy2);
+            add(builder, s, y, -yy2);
+            add(builder, s, -y, yy);
+            add(builder, s, -y, -yy);
+            add(builder, s, -y, yy1);
+            add(builder, s, -y, -yy1);
+            add(builder, s, -y, yy2);
+            add(builder, s, -y, -yy2);
+        }
+
+        // Fails add at 2.04 * eps
+        builder.add(Arguments.of(1.936367217696177, -5.990222602369122E-17, -1.0983273763870391, -8.300320179242541E-17));
+        // Fails add at 2.28 * eps
+        builder.add(Arguments.of(1.8570239083555447, 9.484019916269656E-17, -1.0125292773654282, 8.247363448814862E-17));
+
+        // Cases that fail exact summation to a single double if performed incorrectly.
+        // These require correct propagation of the round-off to the high-part.
+        // The double-double high-part may not be exact but summed with the low-part
+        // it should be <= 0.5 ulp of the IEEE double result.
+        builder.add(Arguments.of(-1.8903599998005227, 1.2825149462328469E-17, 1.8903599998005232, 1.2807862928011876E-17));
+        builder.add(Arguments.of(1.8709715815417154, 2.542250988259237E-17, -1.8709715815417152, 1.982876215341407E-17));
+        builder.add(Arguments.of(-1.8246677074340567, 2.158144877411339E-17, 1.8246677074340565, 2.043199561107511E-17));
+        return builder.build();
+    }
+
+    /**
+     * Adds the two double-double numbers as arguments. Ensured the (x,yy) values is normalized.
+     * The argument {@code t} is used for working.
+     */
+    private static Stream.Builder<Arguments> add(Stream.Builder<Arguments> builder,
+            DD x, double y, double yy) {
+        final DD t = DD.fastTwoSum(y, yy);
+        builder.add(Arguments.of(x.hi(), x.lo(), t.hi(), t.lo()));
+        return builder;
+    }
+
+    // Subtraction must be consistent with addition
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDouble"})
+    void testSubtractDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        DD t;
+        final Supplier<String> msg = () -> String.format("(%s,%s)-%s", x, xx, y);
+        for (final double scale : SCALES) {
+            final double sy = scale * y;
+            s = dd.subtract(sy);
+            t = dd.add(-sy);
+            Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + " lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDouble"})
+    void testAccurateSubtractDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        DD t;
+        final Supplier<String> msg = () -> String.format("(%s,%s)-%s", x, xx, y);
+        for (final double scale : SCALES) {
+            final double sy = scale * y;
+            s = DDExt.subtract(dd, sy);
+            t = DDExt.add(dd, -sy);
+            Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + " lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDoubleDouble"})
+    void testSubtractDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        DD t;
+        final Supplier<String> msg = () -> String.format("(%s,%s)-(%s,%s)", x, xx, y, yy);
+        for (final double scale : SCALES) {
+            final DD sy = DD.of(scale * y, scale * yy);
+            s = dd.subtract(sy);
+            t = dd.add(sy.negate());
+            Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + " lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"addDoubleDouble"})
+    void testAccurateSubtractDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        DD t;
+        final Supplier<String> msg = () -> String.format("(%s,%s)-(%s,%s)", x, xx, y, yy);
+        for (final double scale : SCALES) {
+            final DD sy = DD.of(scale * y, scale * yy);
+            s = DDExt.subtract(dd, sy);
+            t = DDExt.add(dd, sy.negate());
+            Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + " hi: scale=" + scale);
+            Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + " lo: scale=" + scale);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMultiplyDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)*%s", x, xx, y);
+
+        s = dd.multiply(y);
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).multiply(bd(y));
+        // double-double multiplication should be within 16 eps^2 with eps = 2^-53.
+        // a single multiply is 4 eps^2
+        TestUtils.assertEquals(e, s, 4 * EPS, () -> msg.get());
+
+        // Same as if low-part of y is zero
+        s = dd.multiply(DD.of(y));
+        // Handle edge case of y==0 where the sign can be different
+        if (y == 0) {
+            Assertions.assertEquals(hi, s.hi(), 0.0, () -> msg.get() + "y=0 yy=0 hi");
+            Assertions.assertEquals(lo, s.lo(), 0.0, () -> msg.get() + "y=0 yy=0 lo");
+        } else {
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " yy=0 hi");
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " yy=0 lo");
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testMultiplyDouble"})
+    void testAccurateMultiplyDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)*%s", x, xx, y);
+
+        s = DDExt.multiply(DD.of(x, xx), y);
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).multiply(bd(y));
+        // double-double multiplication should be within 16 eps^2 with eps = 2^-53.
+        // a single multiply is 0.5 eps^2
+        TestUtils.assertEquals(e, s, 0.5 * EPS, () -> msg.get());
+
+        // Same as if low-part of y is zero
+        s = DDExt.multiply(DD.of(x, xx), DD.of(y));
+        // Handle edge case of y==0 where the sign can be different
+        if (y == 0) {
+            Assertions.assertEquals(hi, s.hi(), 0.0, () -> msg.get() + "y=0 yy=0 hi");
+            Assertions.assertEquals(lo, s.lo(), 0.0, () -> msg.get() + "y=0 yy=0 lo");
+        } else {
+            Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " yy=0 hi");
+            Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " yy=0 lo");
+        }
+    }
+
+    static Stream<Arguments> testMultiplyDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), signedNormalDouble(rng)));
+        }
+        // Multiply by zero
+        for (int i = 0; i < 3; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), 0.0));
+            builder.add(Arguments.of(s.hi(), s.lo(), -0.0));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Test multiply by an int.
+     * This method exists to support the {@link NativeOperators} interface.
+     */
+    @ParameterizedTest
+    @MethodSource
+    void testMultiplyInt(double x, double xx, int y) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        final Supplier<String> msg = () -> String.format("(%s,%s)*%d", x, xx, y);
+
+        // Test this is consistent with multiplication by a double
+        final DD s = dd.multiply(y);
+        final DD t = dd.multiply((double) y);
+        Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + " hi");
+        Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + " lo");
+    }
+
+    static Stream<Arguments> testMultiplyInt() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), rng.nextInt()));
+        }
+        // Multiply by zero
+        for (int i = 0; i < 3; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), 0));
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMultiplyDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final DD dx = DD.of(x, xx);
+        final DD dy = DD.of(y, yy);
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)*(%s,%s)", x, xx, y, yy);
+
+        s = dx.multiply(dy);
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).multiply(bd(y).add(bd(yy)));
+        // double-double multiplication should be within 16 eps^2 with eps = 2^-53.
+        // This passes at 4 eps^2
+        TestUtils.assertEquals(e, s, 4 * EPS, () -> msg.get());
+
+        // Same if reversed
+        s = dy.multiply(dx);
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " reversed hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " reversed lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testMultiplyDoubleDouble"})
+    void testAccurateMultiplyDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)*(%s,%s)", x, xx, y, yy);
+
+        s = DDExt.multiply(DD.of(x, xx), DD.of(y, yy));
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).multiply(bd(y).add(bd(yy)));
+        // This passes at 0.5 eps^2
+        TestUtils.assertEquals(e, s, 0.5 * EPS, () -> msg.get());
+
+        // Same if reversed
+        s = DDExt.multiply(DD.of(y, yy), DD.of(x, xx));
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " reversed hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " reversed lo");
+    }
+
+    static Stream<Arguments> testMultiplyDoubleDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            final DD t = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), t.hi(), t.lo()));
+        }
+        // Multiply by zero
+        for (int i = 0; i < 5; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), 0.0, 0.0));
+            builder.add(Arguments.of(s.hi(), s.lo(), -0.0, 0.0));
+            builder.add(Arguments.of(s.hi(), s.lo(), 0.0, -0.0));
+            builder.add(Arguments.of(s.hi(), s.lo(), -0.0, -0.0));
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testSquare(double x, double xx) {
+        assertNormalized(x, xx, "x");
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)^2", x, xx);
+
+        s = dd.square();
+        final DD t = dd.multiply(dd);
+        // Consistent with multiply
+        final double hi = t.hi();
+        final double lo = t.lo();
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " lo");
+
+        s = dd.negate().square();
+        Assertions.assertEquals(hi, s.hi(), () -> "negated " + msg.get() + " hi");
+        Assertions.assertEquals(lo, s.lo(), () -> "negated " + msg.get() + " lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testSquare"})
+    void testAccurateSquare(double x, double xx) {
+        assertNormalized(x, xx, "x");
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)^2", x, xx);
+
+        s = DDExt.square(DD.of(x, xx));
+        final DD t = DDExt.multiply(DD.of(x, xx), DD.of(x, xx));
+        // Consistent with multiply
+        final double hi = t.hi();
+        final double lo = t.lo();
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " lo");
+
+        s = DDExt.square(DD.of(x, xx).negate());
+        Assertions.assertEquals(hi, s.hi(), () -> "negated " + msg.get() + " hi");
+        Assertions.assertEquals(lo, s.lo(), () -> "negated " + msg.get() + " lo");
+    }
+
+    static Stream<Arguments> testSquare() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo()));
+        }
+        // Square zero
+        builder.add(Arguments.of(0.0, 0.0));
+        builder.add(Arguments.of(0.0, -0.0));
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testDivideDoubleDouble"})
+    void testDivideDouble(double x, double xx, double y) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)/%s", x, xx, y);
+
+        DD s = DD.of(x, xx).divide(y);
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).divide(bd(y), MC_DIVIDE);
+        // double-double multiplication should be within 16 eps^2 with eps = 2^-53.
+        // This passes with a relative error of 2^-107.
+        TestUtils.assertEquals(e, s, 0.5 * EPS, () -> msg.get());
+
+        // Same as if low-part of y is zero
+        s = DD.of(x, xx).divide(DD.of(y));
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " yy=0 hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " yy=0 lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testDivideDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final Supplier<String> msg = () -> String.format("(%s,%s)/(%s,%s)", x, xx, y, yy);
+
+        final DD s = DD.of(x, xx).divide(DD.of(y, yy));
+        // Check normalized
+        final double hi = s.hi();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).divide(bd(y).add(bd(yy)), MC_DIVIDE);
+        // This passes at 3 eps^2
+        TestUtils.assertEquals(e, s, 3 * EPS, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testDivideDoubleDouble"})
+    void testAccurateDivideDoubleDouble(double x, double xx, double y, double yy) {
+        assertNormalized(x, xx, "x");
+        assertNormalized(y, yy, "y");
+        final Supplier<String> msg = () -> String.format("(%s,%s)/(%s,%s)", x, xx, y, yy);
+
+        final DD s = DDExt.divide(DD.of(x, xx), DD.of(y, yy));
+        // Check normalized
+        final double hi = s.hi();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).divide(bd(y).add(bd(yy)), MC_DIVIDE);
+        // This passes at 1 eps^2
+        TestUtils.assertEquals(e, s, EPS, () -> msg.get());
+    }
+
+    static Stream<Arguments> testDivideDoubleDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 3 * SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            final DD t = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo(), t.hi(), t.lo()));
+        }
+
+        // Fails at 2.233 * eps^2
+        builder.add(Arguments.of(-1.4146588987981588, -7.11198841676502E-17, 1.0758443723302153, -1.0505084507177448E-16));
+        // Fails at 2.947 * eps^2
+        builder.add(Arguments.of(1.1407702754659819, 9.767718050897088E-17, -1.0034167436917367, 1.0394836915953897E-16));
+
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testReciprocalDoubleDouble(double y, double yy) {
+        assertNormalized(y, yy, "y");
+        final Supplier<String> msg = () -> String.format("1/(%s,%s)", y, yy);
+        DD s;
+
+        s = DD.of(y, yy).reciprocal();
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = BigDecimal.ONE.divide(bd(y).add(bd(yy)), MC_DIVIDE);
+        // double-double has 106-bits precision.
+        // This passes with a relative error of 2^-105.
+        TestUtils.assertEquals(e, s, 2 * EPS, () -> msg.get());
+
+        // Same as if using divide
+        s = DD.ONE.divide(DD.of(y, yy));
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " (x,xx)=(1,0) hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " (x,xx)=(1,0) lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testReciprocalDoubleDouble"})
+    void testAccurateReciprocalDoubleDouble(double y, double yy) {
+        assertNormalized(y, yy, "y");
+        final Supplier<String> msg = () -> String.format("1/(%s,%s)", y, yy);
+        DD s;
+
+        s = DDExt.reciprocal(DD.of(y, yy));
+        // Check normalized
+        final double hi = s.hi();
+        final double lo = s.lo();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = BigDecimal.ONE.divide(bd(y).add(bd(yy)), MC_DIVIDE);
+        // double-double has 106-bits precision.
+        // This passes with a relative error of 2^-106.
+        TestUtils.assertEquals(e, s, EPS, () -> msg.get());
+
+        // Same as if using divide
+        s = DDExt.divide(DD.ONE, DD.of(y, yy));
+        Assertions.assertEquals(hi, s.hi(), () -> msg.get() + " (x,xx)=(1,0) hi");
+        Assertions.assertEquals(lo, s.lo(), () -> msg.get() + " (x,xx)=(1,0) lo");
+    }
+
+    static Stream<Arguments> testReciprocalDoubleDouble() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo()));
+        }
+        return builder.build();
+    }
+
+    @Test
+    void testInfiniteOperationsCreateNaN() {
+        // Demonstrate that operations on inf creates NaN.
+        // For this reason special handling of single operations to return the IEEE correct result
+        // for overflow are not be required. It is possible to include a multiply that is safe
+        // against intermediate overflow. But this may never be used. Instead the class is
+        // documented as unsuitable for computations that approach +/- inf, and documented
+        // that the multiply is safe when the exponent is < 996.
+        for (final DD x : new DD[] {DD.of(Double.POSITIVE_INFINITY)}) {
+            for (final DD a : new DD[] {DD.ZERO, DD.ONE}) {
+                assertNaN(x.add(a), () -> String.format("%s.add(%s)", x, a));
+                assertNaN(x.add(a), () -> String.format("%s.add(%s)", x, a));
+                assertNaN(x.add(a), () -> String.format("%s.multiply(%s)", x, a));
+            }
+            for (final double a : new double[] {0, 1}) {
+                assertNaN(x.add(a), () -> String.format("%s.add(%s)", x, a));
+                assertNaN(x.add(a), () -> String.format("%s.add(%s)", x, a));
+                assertNaN(x.add(a), () -> String.format("%s.multiply(%s)", x, a));
+            }
+        }
+    }
+
+    /**
+     * Assert both parts of the DD are NaN.
+     */
+    private static void assertNaN(DD x, Supplier<String> msg) {
+        Assertions.assertEquals(Double.NaN, x.hi(), () -> "hi " + msg.get());
+        Assertions.assertEquals(Double.NaN, x.lo(), () -> "lo " + msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testSqrtSpecialCases(double x, double xx, double hi, double lo) {
+        final DD z = DD.of(x, xx).sqrt();
+        Assertions.assertEquals(hi, z.hi(), "hi");
+        Assertions.assertEquals(lo, z.lo(), "lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testSqrtSpecialCases"})
+    void testAccurateSqrtSpecialCases(double x, double xx, double hi, double lo) {
+        final DD z = DDExt.sqrt(DD.of(x, xx));
+        Assertions.assertEquals(hi, z.hi(), "hi");
+        Assertions.assertEquals(lo, z.lo(), "lo");
+    }
+
+    static Stream<Arguments> testSqrtSpecialCases() {
+        // Note: Cases for non-normalized numbers are not supported
+        // (these are commented out using ///).
+        // The method assumes |x| > |xx|.
+
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final double inf = Double.POSITIVE_INFINITY;
+        ///final double max = Double.MAX_VALUE;
+        final double nan = Double.NaN;
+        builder.add(Arguments.of(1, 0, 1, 0));
+        builder.add(Arguments.of(4, 0, 2, 0));
+        ///builder.add(Arguments.of(0, 1, 1, 0));
+        // x+xx is NaN
+        builder.add(Arguments.of(nan, 3, nan, 0));
+        // x+xx is negative
+        builder.add(Arguments.of(-1, 0, nan, 0));
+        builder.add(Arguments.of(-inf, 3, nan, 0));
+        ///builder.add(Arguments.of(1, -3, nan, 0));
+        ///builder.add(Arguments.of(42, -inf, nan, 0));
+        // x+xx is infinite
+        builder.add(Arguments.of(inf, 0, inf, 0));
+        builder.add(Arguments.of(inf, 3, inf, 0));
+        ///builder.add(Arguments.of(0, inf, inf, 0));
+        ///builder.add(Arguments.of(3, inf, inf, 0));
+        ///builder.add(Arguments.of(max, max, inf, 0));
+        // x+xx is zero
+        final double[] zero = {0.0, -0.0};
+        for (final double x : zero) {
+            for (final double xx : zero) {
+                ///builder.add(Arguments.of(x, xx, x + xx, 0));
+                builder.add(Arguments.of(x, xx, x, 0));
+            }
+        }
+        // Numbers are normalized before computation
+        ///builder.add(Arguments.of(5, -1, 2, 0));
+        ///builder.add(Arguments.of(-1, 5, 2, 0));
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt0.csv"})
+    void testSqrt(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        TestUtils.assertEquals(expected, dd.sqrt(), 2 * EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt512.csv"})
+    void testSqrtBig(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        TestUtils.assertEquals(expected, dd.sqrt(), 2 * EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt-512.csv"})
+    void testSqrtSmall(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        // This test data has a case that fails at 1.01 * 2^-105
+        TestUtils.assertEquals(expected, dd.sqrt(), 2.03 * EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt0.csv"})
+    void testAccurateSqrt(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        TestUtils.assertEquals(expected, DDExt.sqrt(dd), EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt512.csv"})
+    void testAccurateSqrtBig(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        TestUtils.assertEquals(expected, DDExt.sqrt(dd), EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(resources = {"sqrt-512.csv"})
+    void testAccurateSqrtSmall(double x, double xx, BigDecimal expected) {
+        final DD dd = DD.fastTwoSum(x, xx);
+        TestUtils.assertEquals(expected, DDExt.sqrt(dd), EPS, () -> dd.toString());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testScalb(double x, double xx) {
+        final DD dd = DD.of(x, xx);
+        DD s;
+        final Supplier<String> msg = () -> String.format("(%s,%s)", x, xx);
+        // Scales around powers of 2 up to the limit of 2^12 = 4096
+        for (int p = 0; p <= 12; p++) {
+            final int b = 1 << p;
+            for (int i = -1; i <= 1; i++) {
+                final int n = b + i;
+                s = dd.scalb(n);
+                Assertions.assertEquals(Math.scalb(x, n), s.hi(), () -> msg.get() + " hi: scale=" + n);
+                Assertions.assertEquals(Math.scalb(xx, n), s.lo(), () -> msg.get() + " lo: scale=" + n);
+                s = dd.scalb(-n);
+                Assertions.assertEquals(Math.scalb(x, -n), s.hi(), () -> msg.get() + " hi: scale=" + -n);
+                Assertions.assertEquals(Math.scalb(xx, -n), s.lo(), () -> msg.get() + " lo: scale=" + -n);
+            }
+        }
+        // Extreme scaling
+        for (final int n : new int[] {Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE}) {
+            s = dd.scalb(n);
+            Assertions.assertEquals(Math.scalb(x, n), s.hi(), () -> msg.get() + " hi: scale=" + n);
+            Assertions.assertEquals(Math.scalb(xx, n), s.lo(), () -> msg.get() + " lo: scale=" + n);
+        }
+    }
+
+    static Stream<Arguments> testScalb() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi(), s.lo()));
+        }
+        final double[] v = {1, 0, Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE, Math.PI,
+            Math.E, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN};
+        for (final double x : v) {
+            for (final double xx : v) {
+                // Here we do not care if the value is normalized: |x| > |xx|
+                // The test is to check scaling is performed on both numbers independently.
+                builder.add(Arguments.of(x, xx));
+            }
+        }
+        return builder.build();
+    }
+
+
+    @ParameterizedTest
+    @CsvSource({
+        // Non-scalable numbers:
+        // exponent is always zero, (x,xx) is unchanged
+        "0.0, 0.0, 0, 0.0, 0.0",
+        "0.0, -0.0, 0, 0.0, -0.0",
+        "NaN, 0.0, 0, NaN, 0.0",
+        "NaN, NaN, 0, NaN, NaN",
+        "Infinity, 0.0, 0, Infinity, 0.0",
+        "Infinity, NaN, 0, Infinity, NaN",
+        // Normalisation of (1, 0)
+        "1.0, 0, 1, 0.5, 0",
+        "-1.0, 0, 1, -0.5, 0",
+        // Power of 2 with round-off to reduce the magnitude
+        "0.5, -5.551115123125783E-17, -1, 1.0, -1.1102230246251565E-16",
+        "1.0, -1.1102230246251565E-16, 0, 1.0, -1.1102230246251565E-16",
+        "2.0, -2.220446049250313E-16, 1, 1.0, -1.1102230246251565E-16",
+        "0.5, 5.551115123125783E-17, 0, 0.5, 5.551115123125783E-17",
+        "1.0, 1.1102230246251565E-16, 1, 0.5, 5.551115123125783E-17",
+        "2.0, 2.220446049250313E-16, 2, 0.5, 5.551115123125783E-17",
+    })
+    void testFrexpEdgeCases(double x, double xx, int exp, double fx, double fxx) {
+        // Initialize to something so we know it changes
+        final int[] e = {62783468};
+        DD f = DD.of(x, xx).frexp(e);
+        Assertions.assertEquals(exp, e[0], "exp");
+        Assertions.assertEquals(fx, f.hi(), "hi");
+        Assertions.assertEquals(fxx, f.lo(), "lo");
+        // Reset
+        e[0] = 126943276;
+        f = DD.of(-x, -xx).frexp(e);
+        Assertions.assertEquals(exp, e[0], "exp");
+        Assertions.assertEquals(-fx, f.hi(), "hi");
+        Assertions.assertEquals(-fxx, f.lo(), "lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testFrexp(double x, double xx) {
+        Assertions.assertTrue(Double.isFinite(x) && x != 0, "Invalid x: " + x);
+        assertNormalized(x, xx, "x");
+
+        final int[] e = {627846824};
+        final Supplier<String> msg = () -> String.format("(%s,%s)", x, xx);
+        DD f = DD.of(x, xx).frexp(e);
+
+        final double hi = f.hi();
+        final double lo = f.lo();
+        final double ahi = Math.abs(hi);
+        Assertions.assertTrue(0.5 <= ahi && ahi <= 1, () -> msg.get() + " hi");
+
+        // Get the exponent handling sub-normal numbers
+        int exp = Math.abs(x) < 0x1.0p-900 ?
+            Math.getExponent(x * 0x1.0p200) - 200 :
+            Math.getExponent(x);
+
+        // Math.getExponent returns the value for a fractional part in [1, 2) not [0.5, 1)
+        exp += 1;
+
+        // Edge case where the exponent is smaller
+        if (Math.abs(ahi) == 1) {
+            if (hi == 1) {
+                Assertions.assertTrue(lo < 0, () -> msg.get() + " (f,ff) is not < 1");
+            } else {
+                Assertions.assertTrue(lo > 0, () -> msg.get() + " (f,ff) is not > -1");
+            }
+            exp -= 1;
+        }
+
+        Assertions.assertEquals(exp, e[0], () -> msg.get() + " exponent");
+
+        // Check the bits are the same.
+        Assertions.assertEquals(x, Math.scalb(hi, exp), () -> msg.get() + " scaled f hi");
+        Assertions.assertEquals(xx, Math.scalb(lo, exp), () -> msg.get() + " scaled f lo");
+
+        // Check round-trip
+        f = f.scalb(exp);
+        Assertions.assertEquals(x, f.hi(), () -> msg.get() + " scalb f hi");
+        Assertions.assertEquals(xx, f.lo(), () -> msg.get() + " scalb f lo");
+    }
+
+    static Stream<Arguments> testFrexp() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            for (final double scale : SCALES) {
+                builder.add(Arguments.of(s.hi() * scale, s.lo() * scale));
+            }
+        }
+        // Sub-normal numbers
+        final double[] scales = IntStream.of(-1000, -1022, -1023, -1024, -1050, -1074)
+            .mapToDouble(n -> Math.scalb(1.0, n)).toArray();
+        for (int i = 0; i < 5; i++) {
+            s = signedNormalDoubleDouble(rng);
+            for (final double scale : scales) {
+                builder.add(Arguments.of(s.hi() * scale, s.lo() * scale));
+            }
+        }
+        // x is power of 2
+        for (int i = 0; i < 3; i++) {
+            for (final double scale : SCALES) {
+                builder.add(Arguments.of(scale, signedNormalDouble(rng) * scale * 0x1.0p-55));
+                builder.add(Arguments.of(scale, 0));
+            }
+        }
+        // Extreme case should change x to 1.0 when xx is opposite sign
+        builder.add(Arguments.of(0.5, Double.MIN_VALUE));
+        builder.add(Arguments.of(0.5, -Double.MIN_VALUE));
+        builder.add(Arguments.of(-0.5, Double.MIN_VALUE));
+        builder.add(Arguments.of(-0.5, -Double.MIN_VALUE));
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        // Math.pow(x, 0) == 1, even for non-finite values
+        "0.0, 0.0, 0, 1.0, 0.0",
+        "1.23, 0.0, 0, 1.0, 0.0",
+        "1.0, 0.0, 0, 1.0, 0.0",
+        "-2.0, 0.0, 0, 1.0, 0.0",
+        "Infinity, 0.0, 0, 1.0, 0.0",
+        "NaN, 0.0, 0, 1.0, 0.0",
+        // Math.pow(0.0, n) == +/- 0.0
+        "0.0, 0.0, 1, 0.0, 0.0",
+        "0.0, 0.0, 2, 0.0, 0.0",
+        "-0.0, 0.0, 1, -0.0, 0.0",
+        "-0.0, 0.0, 2, 0.0, 0.0",
+        // Math.pow(1, n) == 1
+        "1.0, 0.0, 1, 1.0, 0.0",
+        "1.0, 0.0, 2, 1.0, 0.0",
+        // Math.pow(-1, n) == +/-1 - requires round-off sign propagation
+        "-1.0, 0.0, 1, -1.0, 0.0",
+        "-1.0, 0.0, 2, 1.0, -0.0",
+        "-1.0, -0.0, 1, -1.0, -0.0",
+        "-1.0, -0.0, 2, 1.0, 0.0",
+        // Math.pow(0.0, -n)
+        "0.0, 0.0, -1, Infinity, 0.0",
+        "0.0, 0.0, -2, Infinity, 0.0",
+        "-0.0, 0.0, -1, -Infinity, 0.0",
+        "-0.0, 0.0, -2, Infinity, 0.0",
+        // NaN / Infinite is IEEE pow result for x
+        "Infinity, 0.0, 1, Infinity, 0.0, 0",
+        "-Infinity, 0.0, 1, -Infinity, 0.0, 0",
+        "-Infinity, 0.0, 2, Infinity, 0.0, 0",
+        "Infinity, 0.0, -1, 0.0, 0.0, 0",
+        "-Infinity, 0.0, -1, -0.0, 0.0, 0",
+        "-Infinity, 0.0, -2, 0.0, 0.0, 0",
+        "NaN, 0.0, 1, NaN, 0.0, 0",
+        // Inversion creates infinity (sub-normal x^-n < 2.22e-308)
+        // Signed zeros should match inversion when the result is large and finite.
+        "1e-312, 0.0, -1, Infinity, -0.0",
+        "1e-312, -0.0, -1, Infinity, -0.0",
+        "-1e-312, 0.0, -1, -Infinity, 0.0",
+        "-1e-312, -0.0, -1, -Infinity, 0.0",
+        "1e-156, 0.0, -2, Infinity, -0.0",
+        "1e-156, -0.0, -2, Infinity, -0.0",
+        "-1e-156, 0.0, -2, Infinity, -0.0",
+        "-1e-156, -0.0, -2, Infinity, -0.0",
+        "1e-106, 0.0, -3, Infinity, -0.0",
+        "1e-106, -0.0, -3, Infinity, -0.0",
+        "-1e-106, 0.0, -3, -Infinity, 0.0",
+        "-1e-106, -0.0, -3, -Infinity, 0.0",
+    })
+    void testSimplePowEdgeCases(double x, double xx, int n, double z, double zz) {
+        final DD f = DDExt.simplePow(x, xx, n);
+        Assertions.assertEquals(z, f.hi(), "hi");
+        Assertions.assertEquals(zz, f.lo(), "lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testSimplePow(double x, double xx, int n, double eps) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        final DD s = DDExt.simplePow(x, xx, n);
+        // Check normalized
+        final double hi = s.hi();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).pow(n, MathContext.DECIMAL128);
+        TestUtils.assertEquals(e, s, eps, () -> msg.get());
+    }
+
+    static Stream<Arguments> testSimplePow() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+
+        // Note the negative power is essentially just the same result as x^n combined with
+        // the error of the inverse operation. This is far more accurate than simplePow
+        // and we can use the same relative error for both.
+
+        // Small powers are around the precision of a double 2^-53
+        // No overflow when n < 10
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final int n = rng.nextInt(2, 10);
+            // Some random failures at 2^-53
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 1.5 * 0x1.0p-53));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 1.5 * 0x1.0p-53));
+        }
+
+        // Trigger use of the Taylor series (1+z)^n >> 1
+        // z = xx/x so is <= 2^-53 for a normalized double-double.
+        // n * log1p(z) > log(1 + y)
+        // n ~ log1p(y) / log1p(eps)
+        // y       n
+        // 2^-45   256
+        // 2^-44   512
+        // 2^-43   1024
+        // 2^-40   8192
+        // 2^-35   262144
+        // 2^-b    2^(53-b)
+
+        // Medium powers where the value of a normalized double will not overflow.
+        // Here Math.pow(x, n) alone can be 3 orders of magnitude worse (~10 bits less precision).
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final int n = rng.nextInt(512, 1024);
+            // Some random failures at 2^-53
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-52));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-52));
+        }
+
+        // Large powers to trigger the case for n > 1e8 (100000000), or n * z > 1e-8.
+        // Here Math.pow(x, n) alone can be 9 orders of magnitude worse (~30 bits less precision).
+        // Only applicable when the value will not over/underflow.
+        // Note that we wish to use n > 1e8 to trigger the condition more frequently.
+        // The limit for BigDecimal.pow is 1e9 - 1 so use half of that.
+        // Here we use a value in [0.5, 1) and avoid underflow for the double-double
+        // which occurs when the high part for the result is close to 2^-958.
+        // x^n = 2^-958
+        // x ~ exp(log(2^-958) / n) ~ 0.99999867...
+        final int n = (int) 5e8;
+        final double x = Math.exp(Math.log(0x1.0p-958) / n);
+        for (int i = 0; i < SAMPLES; i++) {
+            final double hi = rng.nextDouble(x, 1);
+            // hi will have an exponent of -1 as it is in [0.5, 1).
+            // Scale some random bits to add on to it.
+            final double lo = signedNormalDouble(rng) * 0x1.0p-53;
+            s = DD.fastTwoSum(hi, lo);
+            // Some random failures at 2^-53
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-52));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-52));
+        }
+
+        // Edge cases
+        // Fails at 2^-53
+        builder.add(Arguments.of(1.093868041452691, 8.212626726872479E-17, 4, 1.5 * 0x1.0p-53));
+        // This creates a result so large it must be safely inverted for the negative power
+        builder.add(Arguments.of(1.987660428759235, 3.7909885615002006e-17, -1006, 0x1.0p-52));
+
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledEdgeCases"})
+    void testSimplePowScaledEdgeCases(double x, double xx, int n, double z, double zz, long exp) {
+        final long[] e = {126384};
+        final DD f = DDExt.simplePowScaled(x, xx, n, e);
+        Assertions.assertEquals(z, f.hi(), "hi");
+        Assertions.assertEquals(zz, f.lo(), "lo");
+        Assertions.assertEquals(exp, e[0], "exp");
+    }
+
+    /**
+     * Test cases of {@link DDExt#simplePowScaled(double, double, int, long[])} where no scaling is
+     * required. It should be the same as {@link DDExt#simplePow(double, double, int)}.
+     */
+    @ParameterizedTest
+    @CsvSource({
+        "1.23, 0.0, 3",
+        "1.23, 0.0, -3",
+        "1.23, 1e-16, 2",
+        "1.23, 1e-16, -2",
+        // No underflow - Do not get close to underflowing the low part
+        "0.5, 1e-17, 900",
+        // x > sqrt(0.5)
+        "0.75, 1e-17, 2000",  // 1.33e-250
+        "0.9, 1e-17, 5000",   // 1.63e-229
+        "0.99, 1e-17, 50000", // 5.75e-219
+        "0.75, 1e-17, 100",   // (safe n)
+        "0.9999999999999999, 1e-17, 2147483647", // (safe x)
+        // No overflow
+        "2.0, 1e-16, 1000",
+        // 2x < sqrt(0.5)
+        "1.5, 1e-16, 1500",   // 1.37e264
+        "1.1, 1e-16, 6000",   // 2.27e248
+        "1.01, 1e-16, 60000", // 1.92e259
+        "2.0, 1e-16, 100",   // (safe n)
+        "1.0000000000000002, 1e-17, 2147483647", // (safe x)
+    })
+    void testSimplePowScaledSafe(double x, double xx, int n) {
+        final long[] exp = {61273468};
+        final DD f = DDExt.simplePowScaled(x, xx, n, exp);
+        // Same
+        DD z = DDExt.simplePow(x, xx, n);
+        final int[] ez = {168168681};
+        z = z.frexp(ez);
+        Assertions.assertEquals(z.hi(), f.hi(), "hi");
+        Assertions.assertEquals(z.lo(), f.lo(), "lo");
+        Assertions.assertEquals(ez[0], exp[0], "exp");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaled"})
+    void testSimplePowScaled(double x, double xx, int n, double eps, double ignored) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        final long[] exp = {61284638};
+        final DD f = DDExt.simplePowScaled(x, xx, n, exp);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).pow(n, MathContext.DECIMAL128);
+        TestUtils.assertScaledEquals(e, f, exp[0], eps, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledLargeN"})
+    void testSimplePowScaledLargeN(double x, double xx, int n, long e, BigDecimal expected, double eps, double ignored) {
+        final long[] exp = {-76967868};
+        final DD f = DDExt.simplePowScaled(x, xx, n, exp);
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        Assertions.assertEquals(e, exp[0], () -> msg.get() + " exponent");
+        TestUtils.assertEquals(expected, f, eps, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testPowNotNormalizedFinite(double x, int n) {
+        final Supplier<String> msg = () -> String.format("(%s,0)^%d", x, n);
+        final DD s = DD.of(x).pow(n);
+        Assertions.assertEquals(Math.pow(x, n), s.hi(), () -> msg.get() + " hi");
+        Assertions.assertEquals(0, s.lo(), () -> msg.get() + " lo");
+    }
+
+    static Stream<Arguments> testPowNotNormalizedFinite() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        for (final int n : new int[] {-2, -1, 0, 1, 2}) {
+            for (final double x : new double[] {Double.NaN, Double.POSITIVE_INFINITY,
+                    Math.nextDown(Double.MIN_NORMAL), Double.MIN_VALUE, 0}) {
+                builder.add(Arguments.of(x, n));
+                builder.add(Arguments.of(-x, n));
+            }
+        }
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testPowEdgeCases(double x, double xx) {
+        final Supplier<String> msg = () -> String.format("(%s,%s)", x, xx);
+        final DD dd = DD.of(x, xx);
+
+        Assertions.assertSame(dd, dd.pow(1), () -> msg.get() + "^1");
+
+        DD s = dd.pow(0);
+        Assertions.assertEquals(1, s.hi(), () -> msg.get() + "^0 hi");
+        Assertions.assertEquals(0, s.lo(), () -> msg.get() + "^0 lo");
+
+        s = dd.pow(-1);
+        final DD t = dd.reciprocal();
+        Assertions.assertEquals(t.hi(), s.hi(), () -> msg.get() + "^-1 hi");
+        Assertions.assertEquals(t.lo(), s.lo(), () -> msg.get() + "^-1 lo");
+    }
+
+    static Stream<Arguments> testPowEdgeCases() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+
+        for (int i = 0; i < 10; i++) {
+            s = signedNormalDoubleDouble(rng);
+            builder.add(Arguments.of(s.hi() * 0.5, s.lo() * 0.5));
+            builder.add(Arguments.of(s.hi(), s.lo()));
+        }
+
+        return builder.build();
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testPow(double x, double xx, int n, double eps) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        final DD s = DD.of(x, xx).pow(n);
+        // Check normalized
+        final double hi = s.hi();
+        Assertions.assertEquals(hi, s.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).pow(n, MathContext.DECIMAL128);
+        TestUtils.assertEquals(e, s, eps, () -> msg.get());
+    }
+
+    static Stream<Arguments> testPow() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+
+        // Note the negative power is essentially just the same result as x^n combined with
+        // the error of the inverse operation. This is more accurate than pow with large
+        // exponents and we can use the same relative error for both.
+
+        // van Mulbregt (2018) pp 22: Error of a compensated pow is ~ 16(n-1) eps^2.
+        // The limit is:
+        // Math.log(16.0 * (Integer.MAX_VALUE-1) * 0x1.0p-106) / Math.log(2) = -71.0
+
+        // Small powers
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final int n = rng.nextInt(2, 5);
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-100));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-100));
+        }
+
+        // Medium powers where the value of a normalized double will not overflow.
+        // Limit the upper exponent to 958 so inversion is safe and avoids sub-normals.
+        // Here Math.pow(x, n) alone can be 3 orders of magnitude worse (~10 bits less precision).
+        for (int i = 0; i < SAMPLES; i++) {
+            s = signedNormalDoubleDouble(rng);
+            final int n = rng.nextInt(830, 958);
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-92));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-92));
+        }
+
+        // Large powers only applicable when the value will not over/underflow.
+        // The limit for BigDecimal.pow is 1e9 - 1 so use half of that.
+        // Here we use a value in [0.5, 1) and avoid underflow for the double-double
+        // which occurs when the high part for the result is close to 2^-958.
+        // x^n = 2^-958
+        // x ~ exp(log(2^-958) / n) ~ 0.99999867...
+        // Here Math.pow(x, n) alone can be 9 orders of magnitude worse (~30 bits less precision).
+        final int n = (int) 5e8;
+        final double x = Math.exp(Math.log(0x1.0p-958) / n);
+        for (int i = 0; i < SAMPLES; i++) {
+            final double hi = rng.nextDouble(x, 1);
+            // hi will have an exponent of -1 as it is in [0.5, 1).
+            // Scale some random bits to add on to it.
+            final double lo = signedNormalDouble(rng) * 0x1.0p-53;
+            s = DD.fastTwoSum(hi, lo);
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-73));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-73));
+        }
+
+        // Edge cases
+        // This creates a result so large it must be safely inverted for the negative power.
+        // Currently overflow protection is not supported so this is disabled.
+        //builder.add(Arguments.of(1.987660428759235, 3.7909885615002006e-17, -1006, 0x1.0p-52));
+
+        return builder.build();
+    }
+
+    /**
+     * Test computing the square of a double (no low part).
+     * This effectively tests squareLowUnscaled via the public power function.
+     */
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledSquare"})
+    void testPowScaledSquare(double x) {
+        final Supplier<String> msg = () -> String.format("%s^2", x);
+
+        // Two product is exact
+        final DD z = DD.twoProd(x, x);
+
+        final long[] e = {67816283};
+        final DD x2 = DD.of(x).pow(2, e);
+        final double hi = Math.scalb(x2.hi(), (int) e[0]);
+        final double lo = Math.scalb(x2.lo(), (int) e[0]);
+
+        // Should be exact
+        Assertions.assertEquals(z.hi(), hi, () -> msg.get() + " hi");
+        Assertions.assertEquals(z.lo(), lo, () -> msg.get() + " lo");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledEdgeCases"})
+    void testPowScaledEdgeCases(double x, double xx, int n, double z, double zz, long exp) {
+        final long[] e = {457578688};
+        final DD f = DD.of(x, xx).pow(n, e);
+        Assertions.assertEquals(z, f.hi(), "hi");
+        Assertions.assertEquals(zz, f.lo(), "lo");
+        Assertions.assertEquals(exp, e[0], "exp");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledSmall", "testPowScaled"})
+    void testPowScaled(double x, double xx, int n, double ignored, double eps) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        final long[] exp = {67868};
+        final DD f = DD.of(x, xx).pow(n, exp);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).pow(n, MathContext.DECIMAL128);
+        TestUtils.assertScaledEquals(e, f, exp[0], eps, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledLargeN"})
+    void testPowScaledLargeN(double x, double xx, int n, long e, BigDecimal expected, double ignored, double eps) {
+        final long[] exp = {6283684};
+        final DD f = DD.of(x, xx).pow(n, exp);
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        Assertions.assertEquals(e, exp[0], () -> msg.get() + " exponent");
+        TestUtils.assertEquals(expected, f, eps, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledEdgeCases"})
+    void testAccuratePowScaledEdgeCases(double x, double xx, int n, double z, double zz, long exp) {
+        final long[] e = {-9089675};
+        final DD f = DDMath.pow(DD.of(x, xx), n, e);
+        Assertions.assertEquals(z, f.hi(), "hi");
+        Assertions.assertEquals(zz, f.lo(), "lo");
+        Assertions.assertEquals(exp, e[0], "exp");
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledSmall", "testPowScaled"})
+    void testAccuratePowScaled(double x, double xx, int n, double ignored, double ignored2) {
+        assertNormalized(x, xx, "x");
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        final long[] exp = {-769854635434L};
+        final DD f = DDMath.pow(DD.of(x, xx), n, exp);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        final BigDecimal e = bd(x).add(bd(xx)).pow(n, MathContext.DECIMAL128);
+        // Javadoc for the method states accuracy is 1 ULP to be conservative.
+        // If correctly performed in triple-double precision it should be exact except
+        // for cases of final rounding error when converted to double-double.
+        // Test typically passes at: 0.5 * eps with eps = 2^-106.
+        // Higher powers may have lower accuracy but are not tested.
+        // Update tolerance to 1.0625 * eps as 1 case of rounding error has been observed.
+        TestUtils.assertScaledEquals(e, f, exp[0], 1.0625 * EPS, () -> msg.get());
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = {"testPowScaledLargeN"})
+    void testAccuratePowScaledLargeN(double x, double xx, int n, long e, BigDecimal expected, double ignored, double ignored2) {
+        final long[] exp = {-657545435};
+        final DD f = DDMath.pow(DD.of(x, xx), n, exp);
+        final Supplier<String> msg = () -> String.format("(%s,%s)^%d", x, xx, n);
+
+        // Check normalized
+        final double hi = f.hi();
+        Assertions.assertEquals(hi, f.doubleValue(), () -> msg.get() + " doubleValue");
+
+        Assertions.assertEquals(e, exp[0], () -> msg.get() + " exponent");
+        // Accuracy is that of a double-double number: 0.5 * eps with eps = 2^-106
+        TestUtils.assertEquals(expected, f, 0.5 * EPS, () -> msg.get());
+    }
+
+    static Stream<Arguments> testPowScaledEdgeCases() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final double inf = Double.POSITIVE_INFINITY;
+        final double nan = Double.NaN;
+        // Math.pow(x, 0) == 1, even for non-finite values (fractional representation)
+        builder.add(Arguments.of(0.0, 0.0, 0, 0.5, 0.0, 1));
+        builder.add(Arguments.of(1.23, 0.0, 0, 0.5, 0.0, 1));
+        builder.add(Arguments.of(1.0, 0.0, 0, 0.5, 0.0, 1));
+        builder.add(Arguments.of(inf, 0.0, 0, 0.5, 0.0, 1));
+        builder.add(Arguments.of(nan, 0.0, 0, 0.5, 0.0, 1));
+        // Math.pow(0.0, n) == +/- 0.0 (no fractional representation)
+        builder.add(Arguments.of(0.0, 0.0, 1, 0.0, 0.0, 0));
+        builder.add(Arguments.of(0.0, 0.0, 2, 0.0, 0.0, 0));
+        builder.add(Arguments.of(-0.0, 0.0, 1, -0.0, 0.0, 0));
+        builder.add(Arguments.of(-0.0, 0.0, 2, 0.0, 0.0, 0));
+        // Math.pow(1, n) == 1 (fractional representation)
+        builder.add(Arguments.of(1.0, 0.0, 1, 0.5, 0.0, 1));
+        builder.add(Arguments.of(1.0, 0.0, 2, 0.5, 0.0, 1));
+        // Math.pow(-1, n) == +/-1 (fractional representation) - requires round-off sign propagation
+        builder.add(Arguments.of(-1.0, 0.0, 1, -0.5, 0.0, 1));
+        builder.add(Arguments.of(-1.0, 0.0, 2, 0.5, -0.0, 1));
+        builder.add(Arguments.of(-1.0, -0.0, 1, -0.5, -0.0, 1));
+        builder.add(Arguments.of(-1.0, -0.0, 2, 0.5, 0.0, 1));
+        // Math.pow(0.0, -n) - No fractional representation
+        builder.add(Arguments.of(0.0, 0.0, -1, inf, 0.0, 0));
+        builder.add(Arguments.of(0.0, 0.0, -2, inf, 0.0, 0));
+        builder.add(Arguments.of(-0.0, 0.0, -1, -inf, 0.0, 0));
+        builder.add(Arguments.of(-0.0, 0.0, -2, inf, 0.0, 0));
+        // NaN / Infinite is IEEE pow result for x
+        builder.add(Arguments.of(inf, 0.0, 1, inf, 0.0, 0));
+        builder.add(Arguments.of(-inf, 0.0, 1, -inf, 0.0, 0));
+        builder.add(Arguments.of(-inf, 0.0, 2, inf, 0.0, 0));
+        builder.add(Arguments.of(inf, 0.0, -1, 0.0, 0.0, 0));
+        builder.add(Arguments.of(-inf, 0.0, -1, -0.0, 0.0, 0));
+        builder.add(Arguments.of(-inf, 0.0, -2, 0.0, 0.0, 0));
+        builder.add(Arguments.of(nan, 0.0, 1, nan, 0.0, 0));
+        // Hit edge case of zero low part
+        builder.add(Arguments.of(0.5, 0.0, -1, 0.5, 0.0, 2));
+        builder.add(Arguments.of(1.0, 0.0, -1, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, -1, 0.5, 0.0, 0));
+        builder.add(Arguments.of(4.0, 0.0, -1, 0.5, 0.0, -1));
+        builder.add(Arguments.of(0.5, 0.0, 2, 0.5, 0.0, -1));
+        builder.add(Arguments.of(1.0, 0.0, 2, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, 2, 0.5, 0.0, 3));
+        builder.add(Arguments.of(4.0, 0.0, 2, 0.5, 0.0, 5));
+        // Exact power of two (representable)
+        // Math.pow(0.5, 123) == 0.5 * Math.scalb(1.0, -122)
+        // Math.pow(2.0, 123) == 0.5 * Math.scalb(1.0, 124)
+        builder.add(Arguments.of(0.5, 0.0, 123, 0.5, 0.0, -122));
+        builder.add(Arguments.of(1.0, 0.0, 123, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, 123, 0.5, 0.0, 124));
+        builder.add(Arguments.of(0.5, 0.0, -123, 0.5, 0.0, 124));
+        builder.add(Arguments.of(1.0, 0.0, -123, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, -123, 0.5, 0.0, -122));
+        // Exact power of two (not representable)
+        builder.add(Arguments.of(0.5, 0.0, 12345, 0.5, 0.0, -12344));
+        builder.add(Arguments.of(1.0, 0.0, 12345, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, 12345, 0.5, 0.0, 12346));
+        builder.add(Arguments.of(0.5, 0.0, -12345, 0.5, 0.0, 12346));
+        builder.add(Arguments.of(1.0, 0.0, -12345, 0.5, 0.0, 1));
+        builder.add(Arguments.of(2.0, 0.0, -12345, 0.5, 0.0, -12344));
+        return builder.build();
+    }
+
+    static Stream<Arguments> testPowScaled() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        DD s;
+
+        // Note the negative power is essentially just the same result as x^n combined with
+        // the error of the inverse operation. This is far more accurate than simplePow
+        // and we can use the same relative error for both.
+
+        // This method uses two epsilon values.
+        // The first is for simplePowScaled, the second for fastPowScaled.
+        // van Mulbregt (2018) pp 22: Error of a compensated pow is ~ 16(n-1) eps^2.
+        // The limit is:
+        // Math.log(16.0 * (Integer.MAX_VALUE-1) * 0x1.0p-106) / Math.log(2) = -71.0
+        // For this test the thresholds are slightly above this limit.
+        // Note: powScaled does not require an epsilon as it is ~ eps^2.
+
+        // Powers that approach and do overflow.
+        // Here the fractional representation does not overflow.
+        // min = 1.5^1700 = 2.26e299
+        // max ~ 2^1799
+        for (int i = 0; i < SAMPLES; i++) {
+            final double x = 1 + rng.nextDouble() / 2;
+            final double xx = signedNormalDouble(rng) * 0x1.0p-52;
+            s = DD.fastTwoSum(x, xx);
+            final int n = rng.nextInt(1700, 1800);
+            // Math.log(16.0 * (1799) * 0x1.0p-106) / Math.log(2) = -91.2
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-52, 0x1.0p-94));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-52, 0x1.0p-94));
+        }
+
+        // Powers that approach and do underflow
+        // Here the fractional representation does not overflow.
+        // max = 0.75^2400 = 1.41e-300
+        // min ~ 2^-2501
+        for (int i = 0; i < SAMPLES; i++) {
+            final double x = 0.5 + rng.nextDouble() / 4;
+            final double xx = signedNormalDouble(rng) * 0x1.0p-53;
+            s = DD.fastTwoSum(x, xx);
+            final int n = rng.nextInt(2400, 2500);
+            // Math.log(16.0 * (2499) * 0x1.0p-106) / Math.log(2) = -90.7
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-51, 0x1.0p-93));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-51, 0x1.0p-93));
+        }
+
+        // Powers where the fractional representation overflow/underflow
+        // x close to sqrt(2) in range [1.4, 1.42). Smallest power:
+        // 1.4^5000 = 4.37e730
+        // 0.71^5000 = 1.96e-744
+        for (int i = 0; i < SAMPLES; i++) {
+            final double x = 1.4 + rng.nextDouble() / 50;
+            final double xx = signedNormalDouble(rng) * 0x1.0p-52;
+            s = DD.fastTwoSum(x, xx);
+            final int n = rng.nextInt(5000, 6000);
+            // Math.log(16.0 * (5999) * 0x1.0p-106) / Math.log(2) = -89.4
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-50, 0x1.0p-90));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-50, 0x1.0p-90));
+        }
+
+        // Large powers
+        // These lose approximately 10-bits of precision in the double result
+        for (int i = 0; i < 20; i++) {
+            final double x = 1.4 + rng.nextDouble() / 50;
+            final double xx = signedNormalDouble(rng) * 0x1.0p-52;
+            s = DD.fastTwoSum(x, xx);
+            final int n = rng.nextInt(500000, 600000);
+            // Math.log(16.0 * (599999) * 0x1.0p-106) / Math.log(2) = -82.8
+            builder.add(Arguments.of(s.hi(), s.lo(), n, 0x1.0p-43, 0x1.0p-85));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, 0x1.0p-43, 0x1.0p-85));
+        }
+
+        // Powers approaching the limit of BigDecimal.pow (n=1e9)
+        // These lose approximately 15-bits of precision in the double result.
+        // Uncomment this to output test cases for testPowScaledLargeN.
+        //for (int i = 0; i < 5; i++) {
+        //    double x = 1.4 + rng.nextDouble() / 50;
+        //    double xx = signedNormalDouble(rng) * 0x1.0p-52;
+        //    s = DD.fastTwoSum(x, xx);
+        //    int n = rng.nextInt(50000000, 60000000);
+        //    builder.add(Arguments.of(s.hi(), s.lo(), n, -0x1.0p-43, -0x1.0p-85));
+        //    builder.add(Arguments.of(s.hi(), s.lo(), -n, -0x1.0p-43, -0x1.0p-85));
+        //}
+
+        // Spot cases
+
+        // Ensure simplePowScaled coverage with cases where:
+        // q = 1
+        builder.add(Arguments.of(0.6726201869238487, -1.260696696499313E-17, 2476, 0x1.0p-52, 0x1.0p-94));
+        builder.add(Arguments.of(0.7373299007207562, 4.2392599349515834E-17, 2474, 0x1.0p-52, 0x1.0p-94));
+        builder.add(Arguments.of(0.7253422761833876, -9.319060725056201E-18, 2477, 0x1.0p-52, 0x1.0p-94));
+        // q > 1
+        builder.add(Arguments.of(1.4057406814073525, 8.718218123265588E-17, 5172, 0x1.0p-51, 0x1.0p-94));
+        builder.add(Arguments.of(1.4123612475347687, -1.8461805152858888E-17, 5318, 0x1.0p-51, 0x1.0p-94));
+        // q = 1, r = 0 (i.e. n = m)
+        builder.add(Arguments.of(1.4192051440957238, 4.240738704702252E-17, 1935, 0x1.0p-51, 0x1.0p-94));
+        builder.add(Arguments.of(1.4146021278694565, 1.7768484484601492E-17, 1917, 0x1.0p-51, 0x1.0p-94));
+        // q = 1, r == 1
+        builder.add(Arguments.of(1.4192051440957238, 4.240738704702252E-17, 1936, 0x1.0p-51, 0x1.0p-94));
+        builder.add(Arguments.of(1.4146021278694565, 1.7768484484601492E-17, 1918, 0x1.0p-51, 0x1.0p-94));
+        // q = 2, r = 0
+        builder.add(Arguments.of(1.4192051440957238, 4.240738704702252E-17, 3870, 0x1.0p-51, 0x1.0p-94));
+        builder.add(Arguments.of(1.4146021278694565, 1.7768484484601492E-17, 3834, 0x1.0p-51, 0x1.0p-94));
+        // q = 2, r == 1
+        builder.add(Arguments.of(1.4192051440957238, 4.240738704702252E-17, 3871, 0x1.0p-51, 0x1.0p-94));
+        builder.add(Arguments.of(1.4146021278694565, 1.7768484484601492E-17, 3835, 0x1.0p-51, 0x1.0p-94));
+
+        // Ensure powScaled coverage with high part a power of 2 and non-zero low part
+        builder.add(Arguments.of(0.5, Math.ulp(0.5) / 2, 7, 0x1.0p-52, 0x1.0p-100));
+        builder.add(Arguments.of(0.5, -Math.ulp(0.5) / 4, 7, 0x1.0p-52, 0x1.0p-100));
+        builder.add(Arguments.of(2, Math.ulp(2.0) / 2, 13, 0x1.0p-52, 0x1.0p-100));
+        builder.add(Arguments.of(2, -Math.ulp(2.0) / 4, 13, 0x1.0p-52, 0x1.0p-100));
+
+        // Verify that if at any point the power x^p is down-scaled to ~ 1 then the
+        // next squaring will create a value above 1 (hence the very small eps for powScaled).
+        // This ensures the value x^e * x^p will always multiply as larger than 1.
+        // pow(1.0 + 2^-53, 2) = 1.000000000000000222044604925031320
+        // pow(1.0 + 2^-106, 2) = 1.000000000000000000000000000000025
+        // pow(Math.nextUp(1.0) - 2^-53 + 2^-54, 2) = 1.000000000000000333066907387546990
+        builder.add(Arguments.of(1.0, 0x1.0p-53, 2, 0x1.0p-52, 0x1.0p-106));
+        builder.add(Arguments.of(1.0, 0x1.0p-106, 2, 0x1.0p-52, 0x1.0p-106));
+        Assertions.assertNotEquals(Math.nextUp(1.0), Math.nextUp(1.0) - 0x1.0p-53, "not normalized double-double");
+        builder.add(Arguments.of(Math.nextUp(1.0), -0x1.0p-53 + 0x1.0p-54, 2, 0x1.0p-52, 0x1.0p-106));
+
+        // Misc failure cases
+        builder.add(Arguments.of(0.991455078125, 0.0, 64, 0x1.0p-53, 0x1.0p-100));
+        builder.add(Arguments.of(0.9530029296875, 0.0, 379, 0x1.0p-53, 0x1.0p-100));
+        builder.add(Arguments.of(0.9774169921875, 0.0, 179, 0x1.0p-53, 0x1.0p-100));
+        // Fails powScaled at 2^-107 due to a rounding error. Requires eps = 1.0047 * 2^-107.
+        // This is a possible error in intermediate triple-double multiplication or
+        // rounding of the triple-double to a double-double. A final rounding error could
+        // be fixed as the power function norm3 normalises intermediates back to
+        // a triple-double from a quad-double result. This discards rounding information
+        // that could be used to correctly round the triple-double to a double-double.
+        builder.add(Arguments.of(0.5319568842468022, -3.190137112420756E-17, 2473, 0x1.0p-51, 0x1.0p-94));
+
+        // Fails fastPow at 2^-94
+        builder.add(Arguments.of(0.5014627401015759, 4.9149107900633496E-17, 2424, 0x1.0p-52, 0x1.0p-93));
+        builder.add(Arguments.of(0.5014627401015759, 4.9149107900633496E-17, -2424, 0x1.0p-52, 0x1.0p-93));
+
+        // Observed to fail simplePow at 2^-52 (requires ~ 1.01 * 2^-52)
+        // This is platform dependent due to the use of java.lang.Math functions.
+        builder.add(Arguments.of(0.7409802960884472, -2.4773863758919158E-17, 2416, 0x1.0p-51, 0x1.0p-93));
+        builder.add(Arguments.of(0.7409802960884472, -2.4773863758919158E-17, -2416, 0x1.0p-51, 0x1.0p-93));
+
+        return builder.build();
+    }
+
+    static Stream<Arguments> testPowScaledLargeN() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        // The scaled BigDecimal power is pre-computed as it takes >10 seconds per case.
+        // Results are obtained from the debugging assertion
+        // message in TestUtils and thus the BigDecimal is rounded to DECIMAL128 format.
+        // simplePowScaled loses ~ 67-bits from a double-double (14-bits from a double).
+        // fastPowScaled   loses ~ 26-bits from a double-double.
+        // powScaled       loses ~ 1-bit from a double-double.
+        builder.add(Arguments.of(1.402774996679172, 4.203934137477261E-17, 58162209, 28399655, "0.5069511623667528687158515355802548", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4024304626662112, -1.4084179645855846E-17, 55066019, 26868321, "0.8324073012126417513056910315887745", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4125582593027008, -3.545476880711939E-17, 50869441, 25348771, "0.5062665858255789519032946906819150", 0x1.0p-38, 0x1.0p-80));
+        builder.add(Arguments.of(1.4119649130236207, -6.64913621578422E-17, 57868054, 28801176, "0.8386830789932243373181320367289536", 0x1.0p-41, 0x1.0p-80));
+        builder.add(Arguments.of(1.4138979166089836, 1.9810424188649008E-17, 57796577, 28879676, "0.8521759805456274150644862351758441", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4145051107021165, 6.919285583856237E-17, -58047003, -29040764, "0.9609529369187483264098384290609811", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4146512942500389, 5.809007274041755E-17, -52177565, -26112078, "0.6333625587966193592039026704846324", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4145748596525067, -1.7347735766459908E-17, -58513216, -29278171, "0.6273407549603278011188148414634989", 0x1.0p-39, 0x1.0p-80));
+        builder.add(Arguments.of(1.4120799563428865, -5.594285001190042E-17, -52544350, -26157721, "0.5406504832406102336189856859270558", 0x1.0p-38, 0x1.0p-80));
+        builder.add(Arguments.of(1.4092258370859025, -8.549761437095368E-17, -51083370, -25281304, "0.7447168954354128135078570760787011", 0x1.0p-39, 0x1.0p-80));
+        return builder.build();
+    }
+
+    static Stream<Arguments> testPowScaledSquare() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < SAMPLES; i++) {
+            builder.add(Arguments.of(signedNormalDouble(rng)));
+        }
+        return builder.build();
+    }
+
+    static Stream<Arguments> testPowScaledSmall() {
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        final double ignored = Double.NaN;
+
+        // Small powers
+        for (int i = 0; i < SAMPLES; i++) {
+            final DD s = signedNormalDoubleDouble(rng);
+            final int n = rng.nextInt(2, 10);
+            builder.add(Arguments.of(s.hi(), s.lo(), n, ignored, 0x1.0p-100));
+            builder.add(Arguments.of(s.hi(), s.lo(), -n, ignored, 0x1.0p-100));
+        }
+
+        return builder.build();
+    }
+
+    // Note: equals and hashcode tests adapted from ComplexTest (since Complex is
+    // also an immutable tuple of two doubles)
+
+    @Test
+    void testEqualsWithNull() {
+        final DD x = DD.of(3.0);
+        Assertions.assertNotEquals(x, null);
+    }
+
+    @Test
+    void testEqualsWithAnotherClass() {
+        final DD x = DD.of(3.0);
+        Assertions.assertNotEquals(x, new Object());
+    }
+
+    @Test
+    void testEqualsWithSameObject() {
+        final DD x = DD.of(3.0);
+        Assertions.assertEquals(x, x);
+    }
+
+    @Test
+    void testEqualsWithCopyObject() {
+        final DD x = DD.of(3.0);
+        final DD y = DD.of(3.0);
+        Assertions.assertEquals(x, y);
+    }
+
+    @Test
+    void testEqualsWithHiDifference() {
+        final DD x = DD.of(0.0, 0.0);
+        final DD y = DD.of(Double.MIN_VALUE, 0.0);
+        Assertions.assertNotEquals(x, y);
+    }
+
+    @Test
+    void testEqualsWithLoDifference() {
+        final DD x = DD.of(1.0, 0.0);
+        final DD y = DD.of(1.0, Double.MIN_VALUE);
+        Assertions.assertNotEquals(x, y);
+    }
+
+    /**
+     * Test {@link DD#equals(Object)}. It should be consistent with
+     * {@link Arrays#equals(double[], double[])} called using the components of two
+     * DD numbers.
+     */
+    @Test
+    void testEqualsIsConsistentWithArraysEquals() {
+        // Explicit check of the cases documented in the Javadoc:
+        assertEqualsIsConsistentWithArraysEquals(DD.of(Double.NaN, 0.0),
+            DD.of(Double.NaN, 1.0), "NaN high and different non-NaN low");
+        assertEqualsIsConsistentWithArraysEquals(DD.of(0.0, Double.NaN),
+            DD.of(1.0, Double.NaN), "Different non-NaN high and NaN low");
+        assertEqualsIsConsistentWithArraysEquals(DD.of(0.0, 0.0), DD.of(-0.0, 0.0),
+            "Different high zeros");
+        assertEqualsIsConsistentWithArraysEquals(DD.of(0.0, 0.0), DD.of(0.0, -0.0),
+            "Different low zeros");
+
+        // Test some values of edge cases
+        final double[] values = {Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, -1, 0, 1};
+        final ArrayList<DD> list = createCombinations(values);
+
+        for (final DD c : list) {
+            final double hi = c.hi();
+            final double lo = c.lo();
+
+            // Check a copy is equal
+            assertEqualsIsConsistentWithArraysEquals(c, DD.of(hi, lo), "Copy DD");
+
+            // Perform the smallest change to the two components
+            final double hiDelta = smallestChange(hi);
+            final double loDelta = smallestChange(lo);
+            Assertions.assertNotEquals(hi, hiDelta, "high was not changed");
+            Assertions.assertNotEquals(lo, loDelta, "low was not changed");
+
+            assertEqualsIsConsistentWithArraysEquals(c, DD.of(hiDelta, lo), "Delta high");
+            assertEqualsIsConsistentWithArraysEquals(c, DD.of(hi, loDelta), "Delta low");
+        }
+    }
+
+    /**
+     * Specific test to target different representations that contain NaN are {@code false}
+     * for {@link DD#equals(Object)}.
+     */
+    @Test
+    void testEqualsWithDifferentNaNs() {
+        // Test some NaN combinations
+        final double[] values = {Double.NaN, 0, 1};
+        final ArrayList<DD> list = createCombinations(values);
+
+        // Is the all-vs-all comparison only the exact same values should be equal, e.g.
+        // (nan,0) not equals (nan,nan)
+        // (nan,0) equals (nan,0)
+        // (nan,0) not equals (0,nan)
+        for (int i = 0; i < list.size(); i++) {
+            final DD c1 = list.get(i);
+            final DD copy = DD.of(c1.hi(), c1.lo());
+            assertEqualsIsConsistentWithArraysEquals(c1, copy, "Copy is not equal");
+            for (int j = i + 1; j < list.size(); j++) {
+                final DD c2 = list.get(j);
+                assertEqualsIsConsistentWithArraysEquals(c1, c2, "Different NaNs should not be equal");
+            }
+        }
+    }
+
+    /**
+     * Test the two DD numbers with {@link DD#equals(Object)} and check the
+     * result is consistent with {@link Arrays#equals(double[], double[])}.
+     *
+     * @param c1 the first DD
+     * @param c2 the second DD
+     * @param msg the message to append to an assertion error
+     */
+    private static void assertEqualsIsConsistentWithArraysEquals(DD c1, DD c2, String msg) {
+        final boolean expected = Arrays.equals(new double[] {c1.hi(), c1.lo()},
+            new double[] {c2.hi(), c2.lo()});
+        final boolean actual = c1.equals(c2);
+        Assertions.assertEquals(expected, actual,
+            () -> String.format("equals(Object) is not consistent with Arrays.equals: %s. %s vs %s", msg, c1, c2));
+    }
+
+    /**
+     * Test {@link DD#hashCode()}. It should be consistent with
+     * {@link Arrays#hashCode(double[])} called using the components of the DD number
+     * and fulfil the contract of {@link Object#hashCode()}, i.e. objects with different
+     * hash codes are {@code false} for {@link Object#equals(Object)}.
+     */
+    @Test
+    void testHashCode() {
+        // Test some values match Arrays.hashCode(double[])
+        final double[] values = {Double.NaN, Double.NEGATIVE_INFINITY, -3.45, -1, -0.0, 0.0, Double.MIN_VALUE, 1, 3.45,
+            Double.POSITIVE_INFINITY};
+        final ArrayList<DD> list = createCombinations(values);
+
+        final String msg = "'equals' not compatible with 'hashCode'";
+
+        for (final DD c : list) {
+            final double hi = c.hi();
+            final double lo = c.lo();
+            final int expected = Arrays.hashCode(new double[] {hi, lo});
+            final int hash = c.hashCode();
+            Assertions.assertEquals(expected, hash, "hashCode does not match Arrays.hashCode({re, im})");
+
+            // Test a copy has the same hash code, i.e. is not
+            // System.identityHashCode(Object)
+            final DD copy = DD.of(hi, lo);
+            Assertions.assertEquals(hash, copy.hashCode(), "Copy hash code is not equal");
+
+            // MATH-1118
+            // "equals" and "hashCode" must be compatible: if two objects have
+            // different hash codes, "equals" must return false.
+            // Perform the smallest change to the two components.
+            // Note: The hash could actually be the same so we check it changes.
+            final double hiDelta = smallestChange(hi);
+            final double loDelta = smallestChange(lo);
+            Assertions.assertNotEquals(hi, hiDelta, "hi was not changed");
+            Assertions.assertNotEquals(lo, loDelta, "lo was not changed");
+
+            final DD cHiDelta = DD.of(hiDelta, lo);
+            final DD cLoDelta = DD.of(hi, loDelta);
+            if (hash != cHiDelta.hashCode()) {
+                Assertions.assertNotEquals(c, cHiDelta, () -> "hi+delta: " + msg);
+            }
+            if (hash != cLoDelta.hashCode()) {
+                Assertions.assertNotEquals(c, cLoDelta, () -> "lo+delta: " + msg);
+            }
+        }
+    }
+
+    /**
+     * Specific test that different representations of zero satisfy the contract of
+     * {@link Object#hashCode()}: if two objects have different hash codes, "equals" must
+     * return false. This is an issue with using {@link Double#hashCode(double)} to create
+     * hash codes and {@code ==} for equality when using different representations of
+     * zero: Double.hashCode(-0.0) != Double.hashCode(0.0) but -0.0 == 0.0 is
+     * {@code true}.
+     *
+     * @see <a
+     * href="https://issues.apache.org/jira/projects/MATH/issues/MATH-1118">MATH-1118</a>
+     */
+    @Test
+    void testHashCodeWithDifferentZeros() {
+        final double[] values = {-0.0, 0.0};
+        final ArrayList<DD> list = createCombinations(values);
+
+        // Explicit test for issue MATH-1118
+        // "equals" and "hashCode" must be compatible
+        for (int i = 0; i < list.size(); i++) {
+            final DD c1 = list.get(i);
+            for (int j = i + 1; j < list.size(); j++) {
+                final DD c2 = list.get(j);
+                if (c1.hashCode() != c2.hashCode()) {
+                    Assertions.assertNotEquals(c1, c2, "'equals' not compatible with 'hashCode'");
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a list of DD numbers using an all-vs-all combination of the provided
+     * values for both the parts.
+     *
+     * @param values the values
+     * @return the list
+     */
+    private static ArrayList<DD> createCombinations(final double[] values) {
+        final ArrayList<DD> list = new ArrayList<>(values.length * values.length);
+        for (final double x : values) {
+            for (final double xx : values) {
+                list.add(DD.of(x, xx));
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Perform the smallest change to the value. This returns the next double value
+     * adjacent to d in the direction of infinity. Edge cases: if already infinity then
+     * return the next closest in the direction of negative infinity; if nan then return
+     * 0.
+     *
+     * @param x the x
+     * @return the new value
+     */
+    private static double smallestChange(double x) {
+        if (Double.isNaN(x)) {
+            return 0;
+        }
+        return x == Double.POSITIVE_INFINITY ? Math.nextDown(x) : Math.nextUp(x);
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testToString(DD x) {
+        final String s = x.toString();
+        Assertions.assertEquals('(', s.charAt(0), "Start");
+        Assertions.assertEquals(')', s.charAt(s.length() - 1), "End");
+        final int i = s.indexOf(',');
+        Assertions.assertEquals(Double.toString(x.hi()), s.substring(1, i));
+        Assertions.assertEquals(Double.toString(x.lo()), s.substring(i + 1, s.length() - 1));
+    }
+
+    static Stream<DD> testToString() {
+        final Stream.Builder<DD> builder = Stream.builder();
+        final UniformRandomProvider rng = createRNG();
+        for (int i = 0; i < 10; i++) {
+            builder.add(signedNormalDoubleDouble(rng));
+        }
+        final double[] values = {Double.NaN, Double.NEGATIVE_INFINITY, -0.0, 0, 1, Double.MIN_VALUE,
+            Double.POSITIVE_INFINITY};
+        for (final double x : values) {
+            for (final double xx : values) {
+                builder.add(DD.of(x, xx));
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Creates a source of randomness.
+     *
+     * @return the uniform random provider
+     */
+    private static UniformRandomProvider createRNG() {
+        return RandomSource.SPLIT_MIX_64.create();
+    }
+
+    /**
+     * Creates a normalized double in the range {@code [1, 2)} with a random sign. The
+     * magnitude is sampled evenly from the 2<sup>52</sup> dyadic rationals in the range.
+     *
+     * @param bits Random bits.
+     * @return the double
+     */
+    private static double makeSignedNormalDouble(long bits) {
+        // Combine an unbiased exponent of 0 with the 52 bit mantissa and a random sign
+        // bit
+        return Double.longBitsToDouble((1023L << 52) | (bits >>> 12) | (bits << 63));
+    }
+
+    /**
+     * Creates a normalized double in the range {@code [1, 2)} with a random sign. The
+     * magnitude is sampled evenly from the 2<sup>52</sup> dyadic rationals in the range.
+     *
+     * @param rng Source of randomness.
+     * @return the double
+     */
+    private static double signedNormalDouble(UniformRandomProvider rng) {
+        return makeSignedNormalDouble(rng.nextLong());
+    }
+
+    /**
+     * Creates a normalized double-double in the range {@code [1, 2)} with a random sign.
+     *
+     * @param rng Source of randomness.
+     * @return the double-double
+     */
+    private static DD signedNormalDoubleDouble(UniformRandomProvider rng) {
+        final double x = signedNormalDouble(rng);
+        // The roundoff must be < 0.5 ULP of the value.
+        // Math.ulp(1.0) = 2^-52.
+        // Generate using +/- [0.25, 0.5) ULP.
+        final double xx = 0x1.0p-54 * signedNormalDouble(rng);
+        // Avoid assertNormalized here:
+        // Since there is no chance of +/- 0.0 we use the faster assertion with no delta.
+        Assertions.assertEquals(x, x + xx, "Failed to generate a random double-double in +/- [1, 2)");
+        return DD.of(x, xx);
+    }
+
+    /**
+     * Create a BigDecimal for the given value.
+     *
+     * @param v Value
+     * @return the BigDecimal
+     */
+    private static BigDecimal bd(double v) {
+        return new BigDecimal(v);
+    }
+
+    /**
+     * Assert the number is normalized such that {@code |xx| <= eps * |x|}.
+     *
+     * @param x High part.
+     * @param xx Low part.
+     * @param name Name of the number.
+     */
+    private static void assertNormalized(double x, double xx, String name) {
+        // Note: Use delta of 0 to allow addition of signed zeros (which may change the sign)
+        Assertions.assertEquals(x, x + xx, 0.0, () -> name + " not a normalized double-double");
+    }
+
+    /**
+     * Compute the max and mean error of operations.
+     * Note: This is not a test and is included for reference.
+     */
+    @Test
+    @org.junit.jupiter.api.Disabled("This fixture creates reference data")
+    void computeError() {
+        final int n = 100_000;
+        final int samples = 10_000_000;
+        final UniformRandomProvider rng = createRNG();
+        final DD[] dd = IntStream.range(0, n).mapToObj(i ->
+            signedNormalDoubleDouble(rng).scalb(rng.nextInt(106))
+        ).toArray(DD[]::new);
+        final BigDecimal[] bd = Arrays.stream(dd).map(DD::bigDecimalValue).toArray(BigDecimal[]::new);
+        computeError(rng, dd, bd, samples, "add(DD)", BigDecimal::add, DD::add, DDExt::add);
+        computeError(rng, dd, bd, samples, "add(double)",
+            (a, b) -> a.add(bd(b.doubleValue())), (a, b) -> a.add(b.hi()), (a, b) -> DDExt.add(a, b.hi()));
+        computeError(rng, dd, bd, samples, "add(double) two-sum",
+            (a, b) -> a.add(bd(b.doubleValue())),
+            (a, b) -> DD.twoSum(a.hi(), a.lo() + b.hi()),
+            (a, b) -> DD.fastTwoSum(a.hi(), a.lo() + b.hi()));
+        computeError(rng, dd, bd, samples, "multiply(DD)", BigDecimal::multiply, DD::multiply, DDExt::multiply);
+        computeError(rng, dd, bd, samples, "multiply(double)",
+            (a, b) -> a.multiply(bd(b.doubleValue())), (a, b) -> a.multiply(b.hi()), (a, b) -> DDExt.multiply(a, b.hi()));
+        computeError(rng, dd, bd, n, "square()",
+            a -> a.multiply(a), DD::square, DDExt::square);
+        computeError(rng, dd, bd, samples, "divide(DD)", (a, b) -> a.divide(b, MC_DIVIDE), DD::divide, DDExt::divide);
+        computeError(rng, dd, bd, samples, "divide(double)",
+            (a, b) -> a.divide(bd(b.doubleValue()), MC_DIVIDE), (a, b) -> a.divide(b.hi()), null);
+        computeError(rng, dd, bd, Math.min(n, samples), "reciprocal()",
+            a -> BigDecimal.ONE.divide(a, MC_DIVIDE), DD::reciprocal, DDExt::reciprocal);
+        // Requires compilation under JDK 9+
+        //computeError(rng, dd, bd, Math.min(n, samples), "sqrt()",
+        //    a -> a.abs().sqrt(MC_DIVIDE), a -> a.abs().sqrt(), a -> DDExt.sqrt(a.abs()));
+    }
+
+    /**
+     * Compute the max and mean error of the operators. This method supports two operators
+     * so they can be compared to a reference implementation.
+     *
+     * @param rng Source of randomness
+     * @param dd DD data
+     * @param bd DD data converted to BigDecimal
+     * @param samples Number of samples
+     * @param operatorName Operator name
+     * @param expected Operator to compute the expected result
+     * @param op1 First operator
+     * @param op2 Second operator
+     */
+    private static void computeError(UniformRandomProvider rng,
+        DD[] dd, BigDecimal[] bd, int samples,
+        String operatorName,
+        BinaryOperator<BigDecimal> reference,
+        BinaryOperator<DD> op1,
+        BinaryOperator<DD> op2) {
+        final ErrorStatistics e1 = new ErrorStatistics();
+        final ErrorStatistics e2 = new ErrorStatistics();
+        final long start = System.nanoTime();
+        for (int n = 0; n < samples; n++) {
+            final int i = rng.nextInt(dd.length);
+            final int j = rng.nextInt(dd.length);
+            final BigDecimal expected = reference.apply(bd[i], bd[j]);
+            final DD actual1 = op1.apply(dd[i], dd[j]);
+            TestUtils.assertEquals(expected, actual1, -1.0, e1::add, () -> operatorName + " op1");
+            if (op2 != null) {
+                final DD actual2 = op2.apply(dd[i], dd[j]);
+                TestUtils.assertEquals(expected, actual2, -1.0, e2::add, () -> operatorName + " op2");
+            }
+        }
+        final long time = System.nanoTime() - start;
+        TestUtils.printf("%-20s    %12.6g  %12.6g  %12.6g  %12.6g  %12.6g  %12.6g   (%.3fs)%n",
+            operatorName,
+            e1.getRMS() / EPS, e1.getMaxAbs() / EPS, e1.getMean() / EPS,
+            e2.getRMS() / EPS, e2.getMaxAbs() / EPS, e2.getMean() / EPS,
+            1e-9 * time);
+    }
+
+    /**
+     * Compute the max and mean error of the operators. This method supports two operators
+     * so they can be compared to a reference implementation.
+     *
+     * @param rng Source of randomness
+     * @param dd DD data
+     * @param bd DD data converted to BigDecimal
+     * @param samples Number of samples
+     * @param operatorName Operator name
+     * @param expected Operator to compute the expected result
+     * @param op1 First operator
+     * @param op2 Second operator
+     */
+    private static void computeError(UniformRandomProvider rng,
+        DD[] dd, BigDecimal[] bd, int samples,
+        String operatorName,
+        UnaryOperator<BigDecimal> reference,
+        UnaryOperator<DD> op1,
+        UnaryOperator<DD> op2) {
+        final ErrorStatistics e1 = new ErrorStatistics();
+        final ErrorStatistics e2 = new ErrorStatistics();
+        final long start = System.nanoTime();
+        final int[] count = {0};
+        final IntSupplier next = samples == dd.length ? () -> count[0]++ : () -> rng.nextInt(dd.length);
+        for (int n = 0; n < samples; n++) {
+            final int i = next.getAsInt();
+            final BigDecimal expected = reference.apply(bd[i]);
+            final DD actual1 = op1.apply(dd[i]);
+            TestUtils.assertEquals(expected, actual1, -1.0, e1::add, () -> operatorName + " op1");
+            if (op2 != null) {
+                final DD actual2 = op2.apply(dd[i]);
+                TestUtils.assertEquals(expected, actual2, -1.0, e2::add, () -> operatorName + " op2");
+            }
+        }
+        final long time = System.nanoTime() - start;
+        TestUtils.printf("%-20s    %12.6g  %12.6g  %12.6g  %12.6g  %12.6g  %12.6g   (%.3fs)%n",
+            operatorName,
+            e1.getRMS() / EPS, e1.getMaxAbs() / EPS, e1.getMean() / EPS,
+            e2.getRMS() / EPS, e2.getMaxAbs() / EPS, e2.getMean() / EPS,
+            1e-9 * time);
+    }
+
+    /**
+     * Class to compute the error statistics.
+     *
+     * <p>This class can be used to summarise relative errors if used as the DoubleConsumer
+     * argument to {@link TestUtils#assertEquals(BigDecimal, DD, double, java.util.function.DoubleConsumer, Supplier)}.
+     * Errors below the precision of a double-double number are treated as zero.
+     *
+     * @see <a href="https://en.wikipedia.org/wiki/Root_mean_square">Wikipedia: RMS</a>
+     */
+    static class ErrorStatistics {
+        /** Sum of squared error. */
+        private DD ss = DD.ZERO;
+        /** Maximum absolute error. */
+        private double maxAbs;
+        /** Number of terms. */
+        private int n;
+        /** Positive sum. */
+        private DD ps = DD.ZERO;
+        /** Negative sum. */
+        private DD ns = DD.ZERO;
+
+        /**
+         * Add the relative error. Values below 2^-107 are ignored.
+         *
+         * @param x Value
+         */
+        void add(double x) {
+            n++;
+            // Ignore errors below 2^-107. This is the effective half ULP limit of DD and
+            // it is not possible to get closer.
+            if (Math.abs(x) <= 0x1.0p-107) {
+                return;
+            }
+            // Overflow is not supported.
+            // Assume the expected and actual are quite close when measuring the RMS.
+            // Here we sum the regular square for speed.
+            ss = add(ss, x * x);
+            // Summing terms of the same sign avoids cancellation in the working sums.
+            if (x < 0) {
+                ns = add(ns, x);
+                maxAbs = maxAbs < -x ? -x : maxAbs;
+            } else {
+                ps = add(ps, x);
+                //ps = ps.add(x);
+                maxAbs = maxAbs < x ? x : maxAbs;
+            }
+        }
+
+        /**
+         * Adds the term to the total.
+         *
+         * @param dd Total
+         * @param x Value
+         * @return the new total
+         */
+        private static DD add(DD dd, double x) {
+            // We use a fastTwoSum here for speed. This is equivalent to a Kahan summation
+            // of the total and is accurate if the total is larger than the terms.
+            return DD.fastTwoSum(dd.hi(), dd.lo() + x);
+        }
+
+        /**
+         * Gets the count of recorded values.
+         *
+         * @return the size
+         */
+        int size() {
+            return n;
+        }
+
+        /**
+         * Gets the maximum absolute error.
+         *
+         * <p>This can be used to set maximum ULP thresholds for test data if the
+         * TestUtils.assertEquals method is used with a large maxUlps to measure the ulp
+         * (and effectively ignore failures) and the maximum reported as the end of
+         * testing.
+         *
+         * @return maximum absolute error
+         */
+        double getMaxAbs() {
+            return maxAbs;
+        }
+
+        /**
+         * Gets the root mean squared error (RMS).
+         *
+         * <p> Note: If no data has been added this will return 0/0 = nan.
+         * This prevents using in assertions without adding data.
+         *
+         * @return root mean squared error (RMS)
+         */
+        double getRMS() {
+            return n == 0 ? 0 : ss.divide(n).sqrt().doubleValue();
+        }
+
+        /**
+         * Gets the mean error.
+         *
+         * <p>The mean can be used to determine if the error is consistently above or
+         * below zero.
+         *
+         * @return mean error
+         */
+        double getMean() {
+            return n == 0 ? 0 : ps.add(ns).divide(n).doubleValue();
+        }
+    }
+}
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java
index 390c84fd..2d629dcc 100644
--- a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java
@@ -22,11 +22,30 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Assertions;
 
 /**
  * Test utilities.
  */
 public final class TestUtils {
+    /** Positive zero bits. */
+    private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0);
+    /** Negative zero bits. */
+    private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0);
+    /** Set this to true to report all deviations to System out when the maximum ULPs is negative. */
+    private static boolean reportAllDeviations = false;
+    /** 2 as a BigDecimal. Used for scaling. */
+    private static final BigDecimal TWO = BigDecimal.valueOf(2);
+    /** 1/2 as a BigDecimal. Used for scaling. */
+    private static final BigDecimal HALF = new BigDecimal(0.5);
+    /** MathContext used for BigDecimal scaling. */
+    private static final MathContext MC_SCALING = new MathContext(2 * MathContext.DECIMAL128.getPrecision());
+
     /**
      * Collection of static methods used in math unit tests.
      */
@@ -57,4 +76,288 @@ public final class TestUtils {
             return null;
         }
     }
+
+
+    /**
+     * Print a formatted message to stdout.
+     * Provides a single point to disable checkstyle warnings on print statements and
+     * enable/disable all print debugging.
+     *
+     * @param format Format string.
+     * @param args Arguments.
+     */
+    static void printf(String format, Object... args) {
+        // CHECKSTYLE: stop regex
+        System.out.printf(format, args);
+        // CHECKSTYLE: resume regex
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided relative error.
+     *
+     * <p>The provided error is relative to the exact result in expected: (e - a) / e.
+     * If expected is zero this division is undefined. In this case the actual must be zero
+     * (no absolute tolerance is supported). The reporting of the error uses the absolute
+     * error and the return value of the relative error is 0. Cases of complete cancellation
+     * should be avoided for benchmarking relative accuracy.
+     *
+     * <p>Note that the actual double-double result is not validated using the high and low
+     * parts individually. These are summed and compared to the expected.
+     *
+     * <p>Set {@code eps} to negative to report the relative error to the stdout and
+     * ignore failures.
+     *
+     * <p>The relative error is signed. The sign of the error
+     * is the same as that returned from Double.compare(actual, expected); it is
+     * computed using {@code actual - expected}.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param eps maximum relative error between the two values
+     * @param msg failure message
+     * @return relative error difference between the values (signed)
+     * @throws NumberFormatException if {@code actual} contains non-finite values
+     */
+    static double assertEquals(BigDecimal expected, DD actual, double eps, Supplier<String> msg) {
+        return assertEquals(expected, actual, eps, null, msg);
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided relative error.
+     *
+     * <p>The provided error is relative to the exact result in expected: (e - a) / e.
+     * If expected is zero this division is undefined. In this case the actual must be zero
+     * (no absolute tolerance is supported). The reporting of the error uses the absolute
+     * error and the return value of the relative error is 0. Cases of complete cancellation
+     * should be avoided for benchmarking relative accuracy.
+     *
+     * <p>Note that the actual double-double result is not validated using the high and low
+     * parts individually. These are summed and compared to the expected.
+     *
+     * <p>Set {@code eps} to negative to report the relative error to the stdout and
+     * ignore failures.
+     *
+     * <p>The relative error is signed. The sign of the error
+     * is the same as that returned from Double.compare(actual, expected); it is
+     * computed using {@code actual - expected}.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param eps maximum relative error between the two values
+     * @param error Consumer for the relative error difference between the values (signed)
+     * @param msg failure message
+     * @return relative error difference between the values (signed)
+     * @throws NumberFormatException if {@code actual} contains non-finite values
+     */
+    static double assertEquals(BigDecimal expected, DD actual, double eps,
+            DoubleConsumer error, Supplier<String> msg) {
+        // actual - expected
+        final BigDecimal delta = new BigDecimal(actual.hi())
+            .add(new BigDecimal(actual.lo()))
+            .subtract(expected);
+        boolean equal;
+        if (expected.compareTo(BigDecimal.ZERO) == 0) {
+            // Edge case. Currently an absolute tolerance is not supported as summation
+            // to zero cases generated in testing all pass.
+            equal = actual.doubleValue() == 0;
+
+            // DEBUG:
+            if (eps < 0) {
+                if (!equal || reportAllDeviations) {
+                    printf("%sexpected 0 != actual <%s + %s> (abs.error=%s)%n",
+                        prefix(msg), actual.hi(), actual.lo(), delta.doubleValue());
+                }
+            } else if (!equal) {
+                Assertions.fail(String.format("%sexpected 0 != actual <%s + %s> (abs.error=%s)",
+                    prefix(msg), actual.hi(), actual.lo(), delta.doubleValue()));
+            }
+
+            return 0;
+        }
+
+        final double rel = delta.divide(expected, MathContext.DECIMAL128).doubleValue();
+        // Allow input of a negative maximum ULPs
+        equal = Math.abs(rel) <= Math.abs(eps);
+
+        if (error != null) {
+            error.accept(rel);
+        }
+
+        // DEBUG:
+        if (eps < 0) {
+            if (!equal || reportAllDeviations) {
+                printf("%sexpected <%s> != actual <%s + %s> (rel.error=%s (%.3f x tol))%n",
+                    prefix(msg), expected.round(MathContext.DECIMAL128), actual.hi(), actual.lo(),
+                    rel, Math.abs(rel) / eps);
+            }
+        } else if (!equal) {
+            Assertions.fail(String.format("%sexpected <%s> != actual <%s + %s> (rel.error=%s (%.3f x tol))",
+                prefix(msg), expected.round(MathContext.DECIMAL128), actual.hi(), actual.lo(),
+                rel, Math.abs(rel) / eps));
+        }
+
+        return rel;
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided relative error.
+     *
+     * <p>Scales the BigDecimal result by the provided exponent and then uses
+     * {@link TestUtils#assertEquals(BigDecimal, DD, double, Supplier)}.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param exp the scale factor of the actual value (2^exp)
+     * @param eps maximum relative error between the two values
+     * @param msg failure message
+     * @return relative error difference between the values (signed)
+     * @throws NumberFormatException if {@code actual} contains non-finite values
+     */
+    static double assertScaledEquals(BigDecimal expected, DD actual, long exp, double eps, Supplier<String> msg) {
+        Assertions.assertEquals(exp, (int) exp, () -> prefix(msg) + "Cannot scale the result");
+        BigDecimal e;
+        // Scale the expected using the opposite scale factor of the actual:
+        // e * 2^exp = expected
+        // e = expected * 2^-exp
+        if (exp < 0) {
+            e = expected.multiply(TWO.pow((int) -exp), MC_SCALING);
+        } else {
+            e = expected.multiply(HALF.pow((int) exp), MC_SCALING);
+        }
+        return TestUtils.assertEquals(e, actual, eps, () -> prefix(msg) + "scale=2^" + exp);
+    }
+
+    // ULP assertions copied from o.a.c.numbers.gamma.TestUtils
+
+    /**
+     * Assert the two numbers are equal within the provided units of least precision.
+     * The maximum count of numbers allowed between the two values is {@code maxUlps - 1}.
+     *
+     * <p>The values -0.0 and 0.0 are considered equal.
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and ignore
+     * failures.
+     *
+     * <p>The ulp difference is signed. It may be truncated to +/-Long.MAX_VALUE. Use of
+     * {@link Math#abs(long)} on the value will always be positive. The sign of the error
+     * is the same as that returned from Double.compare(actual, expected).
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @param msg failure message
+     * @return ulp difference between the values (signed; may be truncated to +/-Long.MAX_VALUE)
+     */
+    static long assertEquals(double expected, double actual, long maxUlps, Supplier<String> msg) {
+        return assertEquals(expected, actual, maxUlps, null, msg);
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided units of least
+     * precision. The maximum count of numbers allowed between the two values is
+     * {@code maxUlps - 1}.
+     *
+     * <p>The values -0.0 and 0.0 are considered equal.
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and
+     * ignore failures.
+     *
+     * <p>The ulp difference is signed. It may be truncated to +/-Long.MAX_VALUE. Use of
+     * {@link Math#abs(long)} on the value will always be positive. The sign of the error
+     * is the same as that returned from Double.compare(actual, expected).
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @param error Consumer for the ulp difference between the values (signed)
+     * @param msg failure message
+     * @return ulp difference between the values (signed; may be truncated to +/-Long.MAX_VALUE)
+     */
+    private static long assertEquals(double expected, double actual, long maxUlps,
+            LongConsumer error, Supplier<String> msg) {
+        final long e = Double.doubleToLongBits(expected);
+        final long a = Double.doubleToLongBits(actual);
+
+        // Code adapted from Precision#equals(double, double, int) so we maintain the delta
+        // for the message and return it for reporting. The sign is maintained separately
+        // to allow reporting errors above Long.MAX_VALUE.
+
+        int sign;
+        long delta;
+        boolean equal;
+        if (e == a) {
+            // Binary equal
+            equal = true;
+            sign = 0;
+            delta = 0;
+        } else if ((a ^ e) < 0L) {
+            // The difference is the count of numbers between each and zero.
+            // This makes -0.0 and 0.0 equal.
+            long d1;
+            long d2;
+            if (a < e) {
+                sign = -1;
+                d1 = e - POSITIVE_ZERO_DOUBLE_BITS;
+                d2 = a - NEGATIVE_ZERO_DOUBLE_BITS;
+            } else {
+                sign = 1;
+                d1 = a - POSITIVE_ZERO_DOUBLE_BITS;
+                d2 = e - NEGATIVE_ZERO_DOUBLE_BITS;
+            }
+            // This may overflow but we report it using an unsigned formatter.
+            delta = d1 + d2;
+            if (delta < 0) {
+                // Overflow
+                equal = false;
+            } else {
+                // Allow input of a negative maximum ULPs
+                equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps);
+            }
+        } else {
+            if (a < e) {
+                sign = -1;
+                delta = e - a;
+            } else {
+                sign = 1;
+                delta = a - e;
+            }
+            // The sign must be negated for negative doubles since the magnitude
+            // comparison (a < e) included the sign bit.
+            sign = a < 0 ? -sign : sign;
+
+            // Allow input of a negative maximum ULPs
+            equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps);
+        }
+
+        assert sign == Double.compare(actual, expected);
+
+        // DEBUG:
+        if (maxUlps < 0) {
+            if (!equal || reportAllDeviations) {
+                printf("%sexpected <%s> != actual <%s> (ulps=%c%s)%n",
+                    prefix(msg), expected, actual, sign < 0 ? '-' : ' ', Long.toUnsignedString(delta));
+            }
+        } else if (!equal) {
+            Assertions.fail(String.format("%sexpected <%s> != actual <%s> (ulps=%c%s)",
+                prefix(msg), expected, actual, sign < 0 ? '-' : ' ', Long.toUnsignedString(delta)));
+        }
+
+        // This may have overflowed.
+        delta = delta < 0 ? Long.MAX_VALUE : delta;
+        delta *= sign;
+        if (error != null) {
+            error.accept(delta);
+        }
+        return delta;
+    }
+
+    /**
+     * Get the prefix for the message.
+     *
+     * @param msg Message supplier
+     * @return the prefix
+     */
+    private static String prefix(Supplier<String> msg) {
+        return msg == null ? "" : msg.get() + ": ";
+    }
 }
diff --git a/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt-512.csv b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt-512.csv
new file mode 100644
index 00000000..25022951
--- /dev/null
+++ b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt-512.csv
@@ -0,0 +1,537 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# High-precision sqrt test data for (x, xx).sqrt().
+# Computed from uniform values in the range [2^e, 2^(e+1)) using JDK JShell (Version 17.0.6):
+#
+# static void sqrtData(long exp) {
+#     MathContext mc = new MathContext(50);
+#     // Target unbiased exponent
+#     exp += 1023L;
+#     for (int i = 0; i < 500; i++) {
+#         // Combine an unbiased exponent with random 52 bit mantissa
+#         double x = Double.longBitsToDouble(
+#             (exp << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         // Combine an unbiased exponent shifted 2^53 with random 52 bit mantissa
+#         double xx = Double.longBitsToDouble(
+#             ((exp - 53) << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         System.out.printf("%s,%s,%s%n", x, xx, new BigDecimal(x).add(new BigDecimal(xx)).sqrt(mc));
+#     }
+# }
+# sqrtData(-512)
+#
+# Note: The value (x, xx) may not be normalized. However these can be added exactly
+# using two-sum to create the double-double number before calling sqrt.
+
+1.4667750364647213E-154,1.461234661902074E-170,1.2111048825203874376442257474864389309548708440216E-77
+1.2615671873741281E-154,1.2717637835964123E-170,1.1231950798388177661723904897780652398091378822467E-77
+1.116898412062488E-154,1.522331264721107E-170,1.0568341459578641676942248303458980190369258716593E-77
+8.10428419279934E-155,1.1131253356453712E-170,9.0023797924767325059571110625844819234963829566897E-78
+8.947469679291762E-155,1.5561584609780262E-170,9.4591065536295569004638875812378259033425609000861E-78
+1.4752480579432242E-154,1.5292844592378555E-170,1.2145978996948843380901135939304010283788618188802E-77
+1.1013299933486992E-154,9.819736545449423E-171,1.0494427060819943941714426904007596648689294186495E-77
+1.0836428237180608E-154,1.2578960004612654E-170,1.0409816634879122468377745726960479008388297746859E-77
+8.36033883927951E-155,1.2996360066681827E-170,9.1434888523361321749258289588855610364075507047049E-78
+1.4550377437003035E-154,1.2575945529462738E-170,1.2062494533471522552490659469134304798768837008096E-77
+9.510212894308635E-155,1.6142557638354654E-170,9.7520320417380894269582371566838115808450851231551E-78
+1.0062669486403222E-154,1.0064996921508186E-170,1.0031285803127744060526967439639211933068365009929E-77
+1.1198407770336352E-154,1.5590915750043405E-170,1.0582252959713424559647794454705243947362968275924E-77
+9.582377180031761E-155,1.3865327960481957E-170,9.7889617324983769291252069487986211074965226758264E-78
+8.05591550699919E-155,1.2291959840581114E-170,8.9754752002326823636612068473728476651040856975600E-78
+1.0470244705571035E-154,9.858111506235862E-171,1.0232421368166498663940438085782533722587071316119E-77
+9.813545237767702E-155,1.0675759222250233E-170,9.9063339524607706455286634059616435500724461754747E-78
+1.2128273766077118E-154,1.4623089947825341E-170,1.1012844213043749137023792832102416282718133400262E-77
+7.55487966355982E-155,1.550370154458608E-170,8.6918810757855065893420307426212606911367996089592E-78
+1.4415028190808537E-154,8.382912871695227E-171,1.2006260113294454949532050485980822493206620599690E-77
+1.3200957685725374E-154,9.399770106837115E-171,1.1489542064732334027583348120964900272538975590250E-77
+1.287803107210239E-154,1.261538222853151E-170,1.1348141289260717829222740038883605034781897715742E-77
+1.4088256242476683E-154,8.90839858452062E-171,1.1869396042965574460607064072882926209827051742453E-77
+1.0649462903009448E-154,8.636212560238831E-171,1.0319623492651972176423738846926652026553735114333E-77
+8.253905446581379E-155,1.2801299963824041E-170,9.0851006855077726664425499568136507245686442010430E-78
+9.912207202681062E-155,1.452901176903212E-170,9.9560068313963423128690707702174249397916733674187E-78
+1.3533152823965756E-154,1.465128519336931E-170,1.1633207994343502355098136354695034434625786129542E-77
+1.3109255462237145E-154,1.0133636286519484E-170,1.1449565695797000798585406922429412255197510009197E-77
+1.257767795775792E-154,9.45328504901242E-171,1.1215024724786798599204803401406065720325698785947E-77
+1.202634823032518E-154,9.083278579195689E-171,1.0966470822614347720980977450460719192192513488912E-77
+1.304307246477952E-154,1.648438761796112E-170,1.1420627156500435039404919909929013760233112390431E-77
+9.908833550838933E-155,1.6478902552344206E-170,9.9543124076145685123631749625979370252940340096060E-78
+8.151489616837146E-155,1.4974129357169799E-170,9.0285600274003534794633337699075396320498873609534E-78
+1.0426640312492396E-154,1.5805652638601589E-170,1.0211092161219777108040061963165905122282104390777E-77
+8.023561633162027E-155,1.5830024039281505E-170,8.9574335795260176832644223670573849530277577269809E-78
+8.29560601072193E-155,1.520555142425695E-170,9.1080217449904736738044126477687846869641323280884E-78
+7.59940453041735E-155,1.3035385942439172E-170,8.7174563551630995442272373589688481459270898607838E-78
+1.1649387318102928E-154,1.3982135677141823E-170,1.0793232749321645706371633732544337455408638439810E-77
+9.285314603297094E-155,8.633048369232872E-171,9.6360337293396263565355752849387030227189338052400E-78
+1.0170287178345195E-154,9.511460004731487E-171,1.0084784171386711227343635547331256918672001263175E-77
+8.13069017696248E-155,1.2801001066916876E-170,9.0170339785111605004452145267491628871753239539052E-78
+1.4866391210275916E-154,1.0843008085576453E-170,1.2192781147168974265096635424942317369695591837141E-77
+9.296737758952748E-155,9.03998290122897E-171,9.6419592194495142236164565601032628492170203117360E-78
+1.3213153407528682E-154,9.570730604951762E-171,1.1494848153641997063994268189705222876925525469687E-77
+8.82394027521443E-155,8.684825611152835E-171,9.3935830625030564478045395079051762577705991737517E-78
+9.22015144968855E-155,1.6054686823529417E-170,9.6021619699360162552269749546170705956106560348956E-78
+9.38542856572473E-155,1.6508693047091838E-170,9.6878421569123077410813399715584884531630066522435E-78
+9.845790023621709E-155,1.5480433207039915E-170,9.9225954385038344837042563173384843984724714804496E-78
+8.04399835658502E-155,1.0588723757788803E-170,8.9688340137305587971338655498699141851517825003013E-78
+7.975779684166204E-155,8.663466683933273E-171,8.9307220784022861678090983006141714653905801509661E-78
+1.215091625360672E-154,1.0869997562499675E-170,1.1023119455765105928187670015663502040139946071392E-77
+1.3854586102186141E-154,1.569687911517337E-170,1.1770550582783348721807027416214260138623374535286E-77
+1.059312773758554E-154,9.853853596839545E-171,1.0292292134206811469885346385543425100606952257943E-77
+9.884252899237575E-155,1.069571482190848E-170,9.9419580059652111403851870853507741650872218491807E-78
+1.1183268767135303E-154,1.555772907235344E-170,1.0575097525382593015778037583097260258759872473861E-77
+7.480831607472397E-155,1.0597575094053955E-170,8.6491800810668741777851946884016072171961241667260E-78
+1.1310876345905512E-154,1.6256563470545674E-170,1.0635260385108355983076982809994458997122254493181E-77
+1.008224084524354E-154,8.601707855153887E-171,1.0041036224037606784365237401635793065661378543887E-77
+1.4196047273442902E-154,1.265590502409405E-170,1.1914716645159004184017258380882554988846451355737E-77
+1.004362790481437E-154,1.6382629592381012E-170,1.0021790211740800475788767668340850871402831346812E-77
+1.2662184658202798E-154,1.2492465211817888E-170,1.1252637316737262947590574421854827159838393830239E-77
+1.1092838571024376E-154,1.3039638020501657E-170,1.0532254540707026175226704339583458907167003184751E-77
+1.455378132676913E-154,1.0265724595597827E-170,1.2063905390365563812531969973015724334665223724211E-77
+1.1292038900468552E-154,9.307125267972496E-171,1.0626400566734040206970325556488636752331247731613E-77
+1.4672653498969503E-154,1.0623236312428976E-170,1.2113072896242928204425525418496031101219881370044E-77
+9.698061346558764E-155,1.6144541632529625E-170,9.8478735504466980783545096305943827135715296781092E-78
+1.0423108922701835E-154,1.4165024040164335E-170,1.0209362821793452578450932326823116761659637466411E-77
+8.886330906648113E-155,9.071105055755037E-171,9.4267337432687224259353560479044109016012594706428E-78
+1.175050520566876E-154,1.5684647375064969E-170,1.0839974725832510803389610099992968709687156104798E-77
+1.2778041743811773E-154,9.290309809657921E-171,1.1304000063611011021212963759456846251070657992511E-77
+1.2712926291325486E-154,9.61506418054207E-171,1.1275161325376008576605374514725613683682716502635E-77
+8.088793049878238E-155,1.3464171407542384E-170,8.9937717615460085797339196700830978846657673211094E-78
+1.1002158441603004E-154,1.4406482987281224E-170,1.0489117427888299298623106968863591431677588826796E-77
+1.4003976719366307E-154,1.5622791277478957E-170,1.1833839917527323239139137975502208901802975943934E-77
+9.06301164110541E-155,1.3375185872407773E-170,9.5199851056109387529520315356044700373721164760791E-78
+7.751799280503073E-155,1.2987332060526537E-170,8.8044302941775137350180120954011170617673397346996E-78
+9.952287872564417E-155,1.0123950138857062E-170,9.9761154126064608516241982383069561443486029481337E-78
+1.3512085662958894E-154,1.175000211681513E-170,1.1624149716413194514472805426400937096637560161358E-77
+9.476589035036792E-155,1.4271597525540577E-170,9.7347773652183711180306916028775154010805023041644E-78
+1.1130167278107345E-154,8.413789535815536E-171,1.0549960795238693855409715737554184425632937401553E-77
+1.486632646948675E-154,1.3832148300845282E-170,1.2192754598320574734914334809962591383907343463711E-77
+1.3464197195434591E-154,1.4402975882106698E-170,1.1603532735953560575599243485201161546999047864084E-77
+1.1301011873000625E-154,9.903132525340435E-171,1.0630621747104271009729777095756880396575376405035E-77
+9.022814071504723E-155,9.062261889749374E-171,9.4988494416454057254817182032726363841303207320981E-78
+1.15613247217119E-154,1.3625201436104857E-170,1.0752360076611972193665874434077625683256169815515E-77
+1.3982990068436185E-154,1.2159399715625375E-170,1.1824969373506295256744960029603653445478367175861E-77
+1.025934617050316E-154,1.2558838653111181E-170,1.0128843058564566987559716129888086921303549219068E-77
+1.1448155677029211E-154,1.5562396620873777E-170,1.0699605449281395226136153641529165958686389601655E-77
+1.1242791504090635E-154,8.818625547243848E-171,1.0603203055723603545755891053538420450402178054438E-77
+1.2520435832194725E-154,9.594602339582187E-171,1.1189475337206265240449946695877096618519119929765E-77
+1.1731641270973689E-154,8.97340984416057E-171,1.0831270133725633165860730571166444580361674881176E-77
+1.1411401105476559E-154,1.5678538988769703E-170,1.0682415974617614713093156714384896598230137303706E-77
+1.2363573089283112E-154,1.6307864352630288E-170,1.1119160530041426679815323116218927629033300292080E-77
+1.3327798232635338E-154,1.1376714468931823E-170,1.1544608366088188301223176059703773086044641217816E-77
+1.068827250583508E-154,1.5708620933694578E-170,1.0338410180407373014901060246474164309419564160022E-77
+1.1834656217420132E-154,1.3448570109916E-170,1.0878720612930609458513581610141162984977740195261E-77
+9.584583488264768E-155,1.0757007732677063E-170,9.7900886044329391724824267194541078367895328409375E-78
+8.40770753066361E-155,8.320189072184756E-171,9.1693552285117682347646374325123301055518957212584E-78
+1.3078972350756337E-154,1.1799198454733639E-170,1.1436333481827267174037129439585611157724449303614E-77
+9.818390800836392E-155,9.555580055853983E-171,9.9087793399774489545073982811449120122836608229765E-78
+7.650323534883076E-155,8.818127088744506E-171,8.7466127928947880409488058706508035962320749274167E-78
+1.1474547619335565E-154,1.2978878025651439E-170,1.0711931487521550778263294402155110475398925154304E-77
+1.4261023936629116E-154,1.5470831020554424E-170,1.1941952912580554010927378449154496546847101633746E-77
+9.015004396699195E-155,1.5367195938341133E-170,9.4947376986935223148467927751936443719524301330556E-78
+8.749404270577194E-155,9.130778930877034E-171,9.3538250307439442092331574671211156669861847308105E-78
+8.441480790620648E-155,9.097130502000537E-171,9.1877531478706200858305440329529683658602108101467E-78
+1.0901050231633425E-154,1.5647275754705627E-170,1.0440809466527691944848935909728957808130094690328E-77
+9.360326152905845E-155,1.1676186188244503E-170,9.6748778560278712445599055464174948424427409652967E-78
+1.3118153440969644E-154,1.0463515792740878E-170,1.1453450764276085608480182811220901358231765854357E-77
+8.548092102332595E-155,9.933627495410522E-171,9.2455892739903799022959826574147099701078299863704E-78
+1.1228646145758784E-154,1.2319877801801627E-170,1.0596530633069856441125416376966514497358432703548E-77
+9.621631738498834E-155,1.5730925146947172E-170,9.8089916599510042079222701929477627464482299455248E-78
+9.36087633174147E-155,8.779464867808003E-171,9.6751621855871081185556215725287262097225086660622E-78
+1.3140043058579546E-154,1.1754648861804307E-170,1.1463002686285800076100871776682241744043684548711E-77
+1.0742482712836273E-154,1.5515319358087183E-170,1.0364594884912904340489007627703929256936805789003E-77
+8.298254912434522E-155,1.3605304125345453E-170,9.1094757875711615724731233119247121080929685737748E-78
+8.670205980910913E-155,1.2341194969099303E-170,9.3113940851576648565350043795945947476957742515309E-78
+1.0660226212216081E-154,1.493464692221501E-170,1.0324837147488614276713973798898002444422086375212E-77
+1.3130742139381106E-154,1.1746284153835206E-170,1.1458945038432249686405949266763646011489386996046E-77
+1.4563460081420275E-154,1.6394046389590242E-170,1.2067916175305609136885916621625976089370375205068E-77
+8.465289339483255E-155,8.7118604244202E-171,9.2007007012962092942738206024700313455982569777858E-78
+7.682552985122965E-155,1.4267839534766557E-170,8.7650173902411436410108217933406587468775343620914E-78
+1.2706227836710377E-154,1.4919555206693058E-170,1.1272190486640286802351753637782180453468474326397E-77
+7.892270208591973E-155,1.0309806415533037E-170,8.8838450057348333911007658983437644424520278156346E-78
+1.1464260533400337E-154,1.1213424099872549E-170,1.0707128715673655189532989061207159523158334128891E-77
+1.4320972094243059E-154,9.045704155608455E-171,1.1967026403515227471698689650784050156496010989796E-77
+1.102996708740342E-154,1.0550293297886136E-170,1.0502365013368856157055034341515423729155736654980E-77
+1.445225528109219E-154,1.3005493682868055E-170,1.2021753316838684331339616808638128921490814590545E-77
+1.2257127614037888E-154,1.581691711869015E-170,1.1071191270156021112488624802931493897203542261302E-77
+9.10973517873555E-155,9.772414220617907E-171,9.5444932703289967073545455201608857555881745512051E-78
+1.38688725437347E-154,1.6502289696358526E-170,1.1776617741836873225659388711186051793660332726376E-77
+1.2957739643299026E-154,1.2698432068637962E-170,1.1383206772829450457740370202608323351497166460945E-77
+1.365243503881878E-154,8.780917131949095E-171,1.1684363499488871032932073801742941040235412709943E-77
+1.2582074867872594E-154,1.6488143590585336E-170,1.1216984830101445824669578389489245571129597930455E-77
+1.2971514239929596E-154,9.280255320825062E-171,1.1389255568266784945441802379039364373171680389084E-77
+1.0815915120431662E-154,1.2642539245298961E-170,1.0399959192435162454464043010192053299753030559016E-77
+1.3926359353273282E-154,8.7918956824486E-171,1.1800999683617181933262350959083733510217843807500E-77
+8.891384720645753E-155,1.1967924100410043E-170,9.4294139375921738378251982685508270561396194641244E-78
+1.3679322156015428E-154,1.6512396677753784E-170,1.1695863437991839604712302478202669234224142612031E-77
+1.4016599277590839E-154,1.107097903352868E-170,1.1839171963271265911628528361340077430954917008194E-77
+1.2906461398545946E-154,1.6281506274707884E-170,1.1360660807605316366292828060592383811322363187183E-77
+8.667072086236941E-155,1.1966122480882706E-170,9.3097111052045769642713581284307107460155274094675E-78
+1.4761337008715E-154,1.3341349375651952E-170,1.2149624277612456765575849701883443797510419496894E-77
+1.4250198614357682E-154,1.3307927658096631E-170,1.1937419576423408798855334417528365334535152518918E-77
+1.2817695692397432E-154,1.0645027619730944E-170,1.1321526263007754450608321174877346709301671619602E-77
+8.673044295596953E-155,9.23608225290035E-171,9.3129180687886187247782403671465618253156883174704E-78
+1.4375998456947986E-154,1.178195255159293E-170,1.1989995186382681233190316334185329923301134327482E-77
+7.506205253076539E-155,1.606912748686653E-170,8.6638359016526508678440664943886268842844210886103E-78
+9.271928887721867E-155,1.4690143854748644E-170,9.6290855680702454057445310775635342064606932764664E-78
+1.3073237557043233E-154,1.2927910474749483E-170,1.1433825937560548146400480215986023304245349443529E-77
+7.491364101739771E-155,1.0741657076121604E-170,8.6552666635637358003673962586868041907729726869893E-78
+9.841905241528111E-155,1.6322295575216687E-170,9.9206377020472393579057863256353949116757149995232E-78
+1.4077178904559354E-154,1.1459708822477936E-170,1.1864728780953804472450107830074330606345051050011E-77
+8.401001781155053E-155,1.0723611871233639E-170,9.1656978900436459255211521928884104388228553500565E-78
+1.462324401753435E-154,1.0733715410381644E-170,1.2092660591257140888516210182361762028234864685249E-77
+1.0495343468744731E-154,1.554938492403684E-170,1.0244678359394565708505503916995721619679331336565E-77
+1.3304324901150346E-154,1.0583071971082993E-170,1.1534437524712831666616779507291641103291779965264E-77
+1.2547974823557549E-154,1.4334280792917945E-170,1.1201774334255064968197343995777667482579674434110E-77
+8.339697446925133E-155,1.389095155777129E-170,9.1321943950647123166282983987957540590594836747504E-78
+7.599812960870721E-155,1.5958776505715718E-170,8.7176906121235585700519487638330206037749452602599E-78
+8.993545025735852E-155,1.1198566770551317E-170,9.4834303001265597477871600467861608925187231657926E-78
+8.668315786765242E-155,8.83270473945206E-171,9.3103790399560226311208807173415862197535711653404E-78
+1.2519737191636688E-154,1.495453959785634E-170,1.1189163146382614966139220951861404359720388477601E-77
+9.512611154442845E-155,9.30243474201565E-171,9.7532615849483122005528692545792815133206023130483E-78
+1.0783804147759962E-154,1.6061830994067302E-170,1.0384509688839412684826454447312877729125391529622E-77
+1.3007745808928541E-154,1.5970263044022448E-170,1.1405150507086060804309484628162120314120163587088E-77
+8.417698602786292E-155,1.63777775209419E-170,9.1748016887485333347836694996787294233278617082389E-78
+1.1896320688470284E-154,1.2461985763191258E-170,1.0907025574587365589301489809044818130227191585692E-77
+1.270184086021405E-154,1.4075549944163911E-170,1.1270244389636833302083791416916147818178408456230E-77
+1.3762424099070943E-154,1.1704495832788401E-170,1.1731335857041577534660267419455213416472497031567E-77
+1.2925567234847361E-154,9.894020846199429E-171,1.1369066467765663556380330201766395026884781035856E-77
+1.1768927518040142E-154,1.444203524584077E-170,1.0848468794276979961339615413015394809867131520421E-77
+1.4884386664748274E-154,8.667763658246543E-171,1.2200158468129942843505546915237738953103864673011E-77
+1.3738044831474792E-154,1.5395476693887032E-170,1.1720940590018701092204444485024992643452433867318E-77
+1.1240768748816815E-154,1.5047065208741818E-170,1.0602249171197976047344031753981821317964503396036E-77
+8.572403601721962E-155,1.4891718524622345E-170,9.2587275592934279474423257000658941351667271255836E-78
+1.1887285444142346E-154,1.4818505077881342E-170,1.0902882850027486105616824218533304736948891657832E-77
+9.568299850420784E-155,1.0343911095702066E-170,9.7817686797535673452851464947466209060321233411025E-78
+1.2545625184702756E-154,1.1753593083021101E-170,1.1200725505387031700480291580146967049719631903694E-77
+1.4741698085425652E-154,1.4983355241368255E-170,1.2141539476287862356103324943069746925542022087273E-77
+7.77739065362823E-155,1.1676168494654259E-170,8.8189515553881070263826084425605129304240371016939E-78
+1.2927047079215589E-154,1.6556594282040924E-170,1.1369717269666643086250303152613279042294065945136E-77
+1.3108786103297578E-154,8.738999303028284E-171,1.1449360725952160520979321329615479634452192138377E-77
+8.211950860328114E-155,1.1639728785945164E-170,9.0619814943135450151300382221686680913191995026729E-78
+1.2205287693289317E-154,1.1759420825521774E-170,1.1047754384167543760212294518106366142696860325390E-77
+8.78192292645125E-155,1.3668075885335816E-170,9.3711914538393946791337088169271957251422668848359E-78
+7.887945065589891E-155,1.1529467985993035E-170,8.8814103978984623111068635221819390096183219500128E-78
+1.3474752235609524E-154,1.2623913207718255E-170,1.1608080046075460161240370071653416615002172245070E-77
+8.02460260528463E-155,1.0951289279581007E-170,8.9580146267376848639745045959637377025379231550036E-78
+1.4085574897997972E-154,8.344993958382031E-171,1.1868266469033282168356373578182475170410367225913E-77
+1.3501357405613374E-154,1.5307486042760004E-170,1.1619534158310037822512381338343543400515504651111E-77
+8.221059414707112E-155,1.2783569034913002E-170,9.0670057983366884475215967159213208334272741431810E-78
+1.2102837188192456E-154,1.005024154975869E-170,1.1001289555407791631973354479292209484016177385806E-77
+7.992481183441778E-155,1.079620242584497E-170,8.9400677757172395440164700504363741685558227816927E-78
+7.982093644806597E-155,1.0617976835194983E-170,8.9342563455536677225091877940743298743951573137483E-78
+1.1638655736724374E-154,8.986755173230336E-171,1.0788260164050723901350211758944422346859591201181E-77
+7.583544157109771E-155,9.243544317634892E-171,8.7083546994307554846034553817017924519257929998796E-78
+9.632687107218623E-155,1.2251287583512308E-170,9.8146253658601887487251527262758503291620282499213E-78
+1.4684887168448596E-154,1.0642257420225879E-170,1.2118121623605119025654317263221792072028681117038E-77
+1.4168070378321785E-154,8.980279503161743E-171,1.1902970376474011107415953829831974746122821246850E-77
+9.970473018313787E-155,9.85637812544935E-171,9.9852255950047455557450385807270962861208432528970E-78
+1.3418233145161756E-154,1.4641254953982055E-170,1.1583709744793227226942848170944336069582203937616E-77
+8.057650436183082E-155,1.1343774989742106E-170,8.9764416313944155236124543329462977806436027140753E-78
+9.943410707424372E-155,9.422255975707942E-171,9.9716652106979471060388066768447715728064902937519E-78
+1.4750944061251132E-154,1.1939329576734353E-170,1.2145346459138633107736460599311331354891531111129E-77
+8.692965973500612E-155,1.2121641403285894E-170,9.3236076566426864859188799504316673658101121638724E-78
+1.309017064191737E-154,9.225379740349608E-171,1.1441228361464240612803232099478933279959600331707E-77
+1.3042580817571362E-154,1.2918988671285287E-170,1.1420411909196341679159327138750383259582981324110E-77
+9.689710428567845E-155,1.2977573381229353E-170,9.8436326773035605644092363177870358614129045181787E-78
+1.124801768599136E-154,1.6011646962144322E-170,1.0605667204844475099042853828724933945391855600230E-77
+1.2959490143041149E-154,1.0366007858090342E-170,1.1383975642560532894544142277486149110133851933504E-77
+1.3217223149125392E-154,8.95140172693217E-171,1.1496618263265677970545603221101989621304592191965E-77
+9.376129816061288E-155,1.1341604062911568E-170,9.6830417824469236057435852333670566929361721835361E-78
+8.285022790989886E-155,1.2934258407368987E-170,9.1022100563488906247966239561584879444650977937959E-78
+1.3236871666231823E-154,8.573523359949867E-171,1.1505160436183331913855475507500602205695132296350E-77
+1.1751080417602412E-154,1.4758867402609378E-170,1.0840240042361799067736848981536777568190534956371E-77
+9.224233619556763E-155,1.5754414464865079E-170,9.6042873861399861377398487595311190298533387500384E-78
+1.3128559011262923E-154,9.133191703628096E-171,1.1457992411964202391915281864961786649490977913891E-77
+1.4298581956100397E-154,8.488908972649424E-171,1.1957667814461312530109775966760820804954874815759E-77
+8.560577664989057E-155,8.921716096085875E-171,9.2523389826513912766689100998093149321150884501221E-78
+8.495228670649953E-155,1.6378613743483515E-170,9.2169564774115942397381035103243245080196692954739E-78
+1.1323303184976457E-154,8.711827072359599E-171,1.0641101063788679998659603416708845838389910248868E-77
+8.456584835332737E-155,1.3828507735278007E-170,9.1959691361665295388834663250314598050136594164259E-78
+1.250351470591192E-154,1.6142797944316996E-170,1.1181911601292473694393877718111535641848931645507E-77
+7.72265024761555E-155,1.0421827118346499E-170,8.7878610865304138720390204212876713525775022966134E-78
+9.124098820442138E-155,1.1216628399282718E-170,9.5520148766855146481380373274248417581057532392577E-78
+1.049544632352832E-154,1.3403490834923852E-170,1.0244728558399349325213959260579407864297377354716E-77
+1.466748724097419E-154,1.2827838706936656E-170,1.2110940195118705843202073513770616816692110793652E-77
+9.543441438509135E-155,1.120593694701589E-170,9.7690539145349872316611391088859211041468440734705E-78
+9.462283593794413E-155,1.5739508562479678E-170,9.7274269947373103767167466560645168763320425563634E-78
+7.513263641992375E-155,8.622599297918996E-171,8.6679084224467761298046758627899910516136900488015E-78
+1.291963001746382E-154,1.2821347605065392E-170,1.1366455039925078333009933194756397977595243929874E-77
+1.3073328060648465E-154,9.31633486017924E-171,1.1433865514622981767792453277605177772647472515884E-77
+8.933216827208003E-155,1.1793028852250698E-170,9.4515696194907248471428357146581572666710835682239E-78
+9.737518391440242E-155,1.6468555853699205E-170,9.8678864968341843828190506681798862078256331304295E-78
+1.1436231095642331E-154,1.357148378027247E-170,1.0694031557669133914897942004814439966922255789030E-77
+7.586877271227553E-155,1.2335970165959509E-170,8.7102682342322582974789056001037417873728260668478E-78
+1.0390274322006107E-154,1.504037006203567E-170,1.0193269505907370087584057965422968638717219741715E-77
+8.70236626510519E-155,1.1891606094371836E-170,9.3286474180907871845253303157978569385325617458454E-78
+1.068583753440178E-154,1.0702305266829879E-170,1.0337232479925069746773925349810463454241926634583E-77
+9.104525165830735E-155,1.3792919581240039E-170,9.5417635507440322966278928823228205024177367038687E-78
+1.2109409709842604E-154,1.6054410138315514E-170,1.1004276309618277632641299038730673705128845273176E-77
+9.599302771161335E-155,9.927210960354544E-171,9.7976031615703523758807925609045021160381590691641E-78
+1.36743933719387E-154,8.820226711308741E-171,1.1693756185220683984592542557983903811392541349308E-77
+1.2765863029692477E-154,1.622305871077073E-170,1.1298611874780228180569447117923528678676640023389E-77
+1.2485051258487718E-154,1.1367379360041808E-170,1.1173652607132422786374564201322721470777193311854E-77
+1.055084383726243E-154,1.368065319969234E-170,1.0271730057425784819250347103551616997958627687941E-77
+8.122377010188458E-155,1.5477877209885507E-170,9.0124230982508026881813982455689255010910330622441E-78
+1.2542119338558978E-154,1.2021995686755088E-170,1.1199160387528602291207967686586556795185659925771E-77
+1.0470373118689196E-154,1.384571490759645E-170,1.0232484116131916786370738859875854621143210875034E-77
+7.791960858386534E-155,1.1545603565905787E-170,8.8272084253100848515015621463255789037034142032059E-78
+9.26664445150958E-155,1.6085879833168931E-170,9.6263411800691861734554256416188076353916675004463E-78
+1.3325922355412213E-154,1.1063255937409607E-170,1.1543795890179371349841508489986359254005897349353E-77
+1.161857522384859E-154,1.0690550710401083E-170,1.0778949496054145064142072472621799483395232376070E-77
+1.0460983143447536E-154,1.4535283190182928E-170,1.0227894770404873994340982037480177975579199912847E-77
+1.4060353441895238E-154,1.2242732552662992E-170,1.1857636122724983896588099422626769237581129058855E-77
+1.4008344139811412E-154,1.3521598729006427E-170,1.1835685083598419905258358015922924261296928462755E-77
+8.079928803970159E-155,1.5160996136704399E-170,8.9888424193386327591467676726881836493166401740582E-78
+1.3402011568344216E-154,1.3294133058233445E-170,1.1576705735374039377118243270024227940859459938656E-77
+1.020021136661236E-154,1.0851547424223887E-170,1.0099609579885928119841838696520909040085740158494E-77
+1.353966412353286E-154,1.099060746115439E-170,1.1636006240773876723774008704188917239888152464735E-77
+8.162158677491781E-155,1.5738635551683142E-170,9.0344666015718841958144562651637881155849168931871E-78
+1.1834758970381323E-154,1.453212522499546E-170,1.0878767839411467508928102756756516350429621537945E-77
+1.3413760488775867E-154,1.405658581916524E-170,1.1581779003579661122326022339744551826763339094468E-77
+1.158361228199651E-154,1.5997563256223553E-170,1.0762719118325309036133500646435406064843131289719E-77
+9.231050090113903E-155,1.4557484188048745E-170,9.6078353910305441593613071428719086779251761296431E-78
+1.2669038318720022E-154,1.3132121018427576E-170,1.1255682262182076746892263136663774488993626168064E-77
+8.556454637128716E-155,9.375649418818877E-171,9.2501106140027954812229515502436379242343254065836E-78
+8.837914462067474E-155,1.0312085080050815E-170,9.4010182757334722242334689638385883142527155812247E-78
+8.583742780403693E-155,1.038645459338911E-170,9.2648490437803105916938574343551000608303086556634E-78
+1.1801516295994758E-154,1.3105291143919371E-170,1.0863478400583654416766916619073124236652012216323E-77
+9.066423961701248E-155,1.0630256192711822E-170,9.5217771249390465114335957508078562131173954802027E-78
+1.1874291773427175E-154,1.6216322902360497E-170,1.0896922397368523118598363030914003669888140986275E-77
+9.966968283835791E-155,1.16407372256656E-170,9.9834704806674278316435816377058813201707437969991E-78
+1.2726590759892804E-154,1.2252836481383256E-170,1.1281219242569840739844161537993689424613251113685E-77
+1.2086323119744688E-154,1.0059091404783635E-170,1.0993781478519885916370092438622531310433317453372E-77
+1.2761703019496443E-154,1.3027691054560287E-170,1.1296770786156743161722123077528176305584346247592E-77
+7.722748585623561E-155,1.3206808166146992E-170,8.7879170374005937208342420844151373825386097151820E-78
+1.0569919050106868E-154,1.3202344032946224E-170,1.0281011161411541507476896230978978484286053104378E-77
+1.0820633066798303E-154,1.2898519630882467E-170,1.0402227197479539901350402675490036093632297336496E-77
+9.267079775037076E-155,1.1789773710385599E-170,9.6265672879989143288512165270257257050853901873048E-78
+1.0416536988877457E-154,1.3823675664464957E-170,1.0206143732515948735235281854646108396527197800368E-77
+1.2159716976480393E-154,8.855097331945153E-171,1.1027110671649393644656117126441492840447245850002E-77
+1.057793208085267E-154,1.3100307627571384E-170,1.0284907428291550075039529733279166508362179070757E-77
+1.3642872293458531E-154,1.461763442482238E-170,1.1680270670433340858780334090043418652574596319447E-77
+8.930553419889E-155,9.060060212462894E-171,9.4501605382601840338581012038405416286225489465863E-78
+9.348914684996518E-155,1.5512482838813374E-170,9.6689785835922293735491654012870298059172868385949E-78
+1.1173326484612152E-154,1.1023412015466514E-170,1.0570395680679201963611487645048021288413786186528E-77
+1.2388010063038849E-154,9.13870762835648E-171,1.1130143783006062287797769645627805419127837775388E-77
+1.0257899298554295E-154,1.3680399764279754E-170,1.0128128799810109353993041470498867129203113626804E-77
+8.342451118153984E-155,9.009781383855662E-171,9.1337019428893039904265577744627942110338111646132E-78
+1.358370739054366E-154,1.0357745199397987E-170,1.1654916297658967402854123747745362634720990341095E-77
+8.880909108075516E-155,1.2806732283927435E-170,9.4238575477749649438186527901697627090535817086581E-78
+8.792457173529638E-155,1.4861887363003605E-170,9.3768103177624530500308375597774764883014630072447E-78
+1.1118736680659734E-154,1.2039121392299903E-170,1.0544542038732519103855731096499361197120161104348E-77
+1.273122276701232E-154,1.0082034744721128E-170,1.1283272028543990468287691839338450348904628186445E-77
+1.46541657759113E-154,1.2422028813073718E-170,1.2105439180761391075500849051359477016902965041124E-77
+1.2534125845335638E-154,9.388805666873205E-171,1.1195591027424876102761171212248310640142108614411E-77
+1.4203230432369506E-154,1.3911413300727544E-170,1.1917730670043482613645724711008731005732409626152E-77
+9.129083338123266E-155,9.176114120581498E-171,9.5546236650761222124284064698754700028584894076440E-78
+7.752325761004119E-155,1.521899672488267E-170,8.8047292752271041306885800903129757708486719486562E-78
+1.0913023241551265E-154,1.5539664030447168E-170,1.0446541648579814425642031693404947811113129581872E-77
+1.4081255709817221E-154,1.1915950130336598E-170,1.1866446692172523514704350524848820906527843642050E-77
+1.1827756304146545E-154,9.611380090694051E-171,1.0875548861619144227168125322203182294427283818324E-77
+1.4856147544925055E-154,1.2968204733798097E-170,1.2188579714193551829171030463412666329677579691007E-77
+1.2998006759546343E-154,1.277244958993947E-170,1.1400880123721301454250963874950254795373997992357E-77
+1.4041252045401961E-154,8.64904297466812E-171,1.1849578914628975963288725297086607890078922227988E-77
+8.470337355741095E-155,9.291824974771602E-171,9.2034435706104568025724345369147101401836471450359E-78
+9.714686573917239E-155,1.6300223081920647E-170,9.8563109599470534220496660646213318333392401592013E-78
+1.0801092622180571E-154,1.5102873976615536E-170,1.0392830520209868699157216317709988955094325359773E-77
+1.4070925952940295E-154,1.5520265946331517E-170,1.1862093387315873080091260070793836306866408872473E-77
+8.476150794415815E-155,9.290775770729801E-171,9.2066013242758676911448143852156116973223924405579E-78
+8.560129538583517E-155,1.155484227497343E-170,9.2520968102282185827870270871606873528498790044719E-78
+1.4842395867631929E-154,1.2505203392249878E-170,1.2182937194138337160435415159392582051917592555071E-77
+9.480490468355564E-155,1.605980854040537E-170,9.7367810226766246819117909872281116813777205917353E-78
+8.168880691408449E-155,1.2008052572947884E-170,9.0381860411304050264815858224057682658249823367143E-78
+1.4707349052097234E-154,1.6104321391016486E-170,1.2127385972293137016487022017095960807092800763091E-77
+1.1504877995020223E-154,1.5291468989001258E-170,1.0726079430537620346786215513793942870515665770156E-77
+1.3011328421210264E-154,1.1133485539242153E-170,1.1406721010531582663768396675076420268217516951219E-77
+1.239136866564761E-154,1.3291473013076768E-170,1.1131652467467537045974000115937344555079112257855E-77
+1.3029302958842735E-154,1.1892043145087336E-170,1.1414597215339109998382729427668778702338825927835E-77
+7.475954846967597E-155,1.3605897488567374E-170,8.6463604175211192536920108858461298245448583457917E-78
+8.74701655275734E-155,1.1203405063173125E-170,9.3525486113451134377814596325180109380857823144341E-78
+1.0189637904544877E-154,9.807440053592444E-171,1.0094373633140829195369895719789592915425001526889E-77
+1.3695574675095689E-154,1.1546516928867763E-170,1.1702809352927052561943070048795652505231257383905E-77
+1.3281162907640323E-154,1.251771504030402E-170,1.1524392785583249391181155118221641796376683533554E-77
+1.2162036341436637E-154,9.741748189468542E-171,1.1028162286363325989106546773085045463321871039937E-77
+1.4855585574611084E-154,1.091828568267313E-170,1.2188349180512956085127607316784385853093891839864E-77
+1.4818054060039599E-154,1.0289139728847417E-170,1.2172942972034166299675855192566411502684830692218E-77
+1.0236713779591194E-154,1.3635068974703154E-170,1.0117664641403763973585862034703783867931001803613E-77
+8.862331887653564E-155,8.999804253258797E-171,9.4139959037879153851332866781388858253203400154600E-78
+1.0179634959832423E-154,1.0214306334111395E-170,1.0089417703630088286419799910506765123017601283968E-77
+1.1112765482758172E-154,1.3026068804619763E-170,1.0541710242061376731806348281769010065559677382760E-77
+1.17482752962224E-154,1.1428410648496898E-170,1.0838946118614300479494248013385214204725186493580E-77
+1.0662318743020774E-154,1.0303133297766404E-170,1.0325850445857123333069986456150406009601840900177E-77
+1.055412832190514E-154,9.53674223418171E-171,1.0273328731187929986820180311595162125682263018978E-77
+7.605395558217355E-155,1.6154154303111386E-170,8.7208919029061223892049275728689262292150394163186E-78
+7.807146332015586E-155,9.370298639463668E-171,8.8358057538719054028996477817846716438512079880491E-78
+1.0899200533363516E-154,1.2998955014896287E-170,1.0439923626810455514403318014167891499799075379026E-77
+1.418042699362088E-154,1.5108365476135492E-170,1.1908159804781291612805846030954506237934261912951E-77
+1.0231444542277576E-154,1.1341169287828609E-170,1.0115060327194088071822208659896603084435759339627E-77
+1.284497362287139E-154,1.5113172656622205E-170,1.1333566791999503433541960782332393347023078535521E-77
+1.4393932836559348E-154,8.576607587694443E-171,1.1997471748897493929794004152269373016730081153495E-77
+9.176018221798349E-155,1.2880431326730878E-170,9.5791535230407231900308529867347050880740445632916E-78
+9.132831325803204E-155,1.0103443972587027E-170,9.5565848114288220574932495482823667862168143405445E-78
+1.393943855154918E-154,1.0260320724174283E-170,1.1806539946804559622425986473300722209970239119408E-77
+1.1236698682159688E-154,9.049318017992495E-171,1.0600329561933293381620728784808111945457138344074E-77
+9.348115755537675E-155,8.74840547232447E-171,9.6685654341984342811650002239969483405946914156046E-78
+7.737402633153924E-155,1.3075281928197225E-170,8.7962506974016636643205884901339079179200794588955E-78
+1.371111008135038E-154,1.0512928850577879E-170,1.1709444940453147453691498806536781421433825793532E-77
+1.4635021355722279E-154,8.631154032455167E-171,1.2097529233575870931001652877585483749725924886522E-77
+1.0759209563990742E-154,1.2478132838001807E-170,1.0372660971993031974204201823839028179376114348740E-77
+7.767417925982737E-155,8.854175465171117E-171,8.8132955958499078098040211206900163177776680604748E-78
+1.1456819748440537E-154,1.4837436924281675E-170,1.0703653464327279892180940010215366446615787714870E-77
+1.1156160943166489E-154,1.4384878290637706E-170,1.0562272929235681330791523348848911708506868044564E-77
+7.636409352000425E-155,9.653354389696464E-171,8.7386551322273988875543835210443127389891324639606E-78
+1.2123175976299603E-154,1.635525286844305E-170,1.1010529495124021747103286806165308944227464882908E-77
+9.996693811768793E-155,1.5000417947125448E-170,9.9983467692257974644581654901559259413300880562971E-78
+9.044107095031842E-155,1.5866335004986052E-170,9.5100510487756284804717256758706840470966515603620E-78
+1.0965864471811071E-154,1.3718271923239393E-170,1.0471802362445097392791690550867856458711295505735E-77
+1.3321863076148084E-154,1.1329373998602564E-170,1.1542037548088329268694170703663745946894475958586E-77
+1.0554153086173344E-154,1.125024550904767E-170,1.0273340783880064016322508097406298571130165260784E-77
+9.764854597222932E-155,1.1877975490068016E-170,9.8817278839395962174766617173666746487811791619782E-78
+8.86308132510308E-155,1.44040732419847E-170,9.4143939396559568799606533568422522657667271591564E-78
+1.4891085882783226E-154,1.355269200692552E-170,1.2202903704767659660110584546753409759678926820171E-77
+1.1412361463912372E-154,1.5078807551172506E-170,1.0682865469485410162604584463673227598080542687821E-77
+8.174747157977417E-155,9.837580931664228E-171,9.0414308369734366592567964915365690354295917684087E-78
+1.1983545614491445E-154,1.0842627538826235E-170,1.0946938208691709793512363063095957540551365161583E-77
+1.4187512867134265E-154,9.447743348336149E-171,1.1911134650877835728407454289280533584099956100232E-77
+1.1272608581610191E-154,1.5537320528882322E-170,1.0617254156141404432958207212855815589856228013276E-77
+1.3311605068026557E-154,1.4790245523000658E-170,1.1537592932681651318464847374099056107307629828624E-77
+1.347221909027272E-154,9.420114623033676E-171,1.1606988881821469549333846344282629362228991303487E-77
+1.2554743845730902E-154,1.0120014687840808E-170,1.1204795333128982752618816585059087654708594838661E-77
+1.2995120361910966E-154,1.047501058242489E-170,1.1399614187292027228433361240091169008990310938018E-77
+1.249561268565785E-154,1.5882559246565723E-170,1.1178377648683127075800938484051907868741943360927E-77
+1.4806605635466589E-154,9.004340111664508E-171,1.2168239657184020931614660865630977986734858303445E-77
+1.2194672602961942E-154,1.6284046691130529E-170,1.1042949154533830334178151133564228294097611016866E-77
+8.694899215329513E-155,1.103599594824056E-170,9.3246443446007711392570032140719260221767609367590E-78
+1.1156614146353148E-154,1.5473237531216356E-170,1.0562487465721864545095130372372375475434939178853E-77
+1.4115246442412732E-154,8.813923483588815E-171,1.1880760262884161258286581873583863599385962777960E-77
+1.0603911430342774E-154,1.0106123145953029E-170,1.0297529524280459769502054024114463279569974209641E-77
+8.49941000180567E-155,8.950107961826737E-171,9.2192244802942459270107718404408290362121449474518E-78
+8.258725774959353E-155,1.5760952977441853E-170,9.0877531738925185333600046120420440107170700492718E-78
+1.0756265467633493E-154,1.4343189956154404E-170,1.0371241713330903782756781334270054808200619866864E-77
+7.716588592972588E-155,9.07565286499728E-171,8.7844115300756425337769132093038681788226107220690E-78
+8.304895320882815E-155,1.326309332915551E-170,9.1131198394857160609988373070165760798028332364971E-78
+9.54653592536689E-155,1.0980963915589765E-170,9.7706376073247602970118539107281961103132523756814E-78
+9.378201546930395E-155,1.297841222588774E-170,9.6841114961210540536289844655986031302894788010716E-78
+1.4560478773599364E-154,9.129811050958842E-171,1.2066680891446232605093520171102130343778297344551E-77
+9.980768260545835E-155,1.2661929911612757E-170,9.9903795025743824004382277929920754568217101294536E-78
+1.2354101561214533E-154,1.27988620024488E-170,1.1114900611887869677313838006540901295074855666964E-77
+1.281124386678793E-154,1.5317983390223832E-170,1.1318676542241116303018973938197152380178701574413E-77
+1.0300122494952898E-154,1.2473389752999875E-170,1.0148951913844551946676146400448644932151749760523E-77
+7.779881502714723E-155,8.893666423437184E-171,8.8203636561735501759708487146545244262793354308754E-78
+1.1980247716145201E-154,1.0796857794243975E-170,1.0945431794198528519662517311160549802364329296666E-77
+1.2577673285484989E-154,1.3640806118700108E-170,1.1215022641744861801385849286317687704840688372891E-77
+1.3801898448614138E-154,1.058216012493091E-170,1.1748148130073155856999793822884493038743403212111E-77
+1.315638262633059E-154,1.5238306530772258E-170,1.1470127560899482855501966440035399230129062410750E-77
+1.2892446439448592E-154,1.3160109828547725E-170,1.1354490935065558256962820824056178610198279956262E-77
+8.91064101642043E-155,1.1978925723625872E-170,9.4396191747445148519396668075094341758469319681343E-78
+7.485417603212445E-155,1.0509049895079727E-170,8.6518307907704977233815679956414257242918908755646E-78
+1.4471733092586696E-154,9.078039667544053E-171,1.2029851658514620693210178329796694070472695309314E-77
+1.0618714689170319E-154,1.296020517775455E-170,1.0304714789439987437897578439448771907729335868931E-77
+1.1367301901865986E-154,1.3030433413489538E-170,1.0661754968984227806595672003542924888457343790984E-77
+1.1029900437838004E-154,1.5070358576060427E-170,1.0502333282579640827635804584555725529777962903125E-77
+1.0392743622824636E-154,1.3039931655059107E-170,1.0194480674769381140109373598345681884223443351992E-77
+1.2303380579730118E-154,1.1129856916390985E-170,1.1092060484747691440926106462845631807450577223226E-77
+1.3349512802111531E-154,1.1234559131240111E-170,1.1554009175222050348309519038633542869034268277776E-77
+1.3264245356430887E-154,1.4742048871956804E-170,1.1517050558381207658087905531733831916287984054982E-77
+1.2983384420947541E-154,1.5699987359671327E-170,1.1394465507845263908281384367408138908106579325617E-77
+1.301896886080366E-154,1.3190198907786832E-170,1.1410069614513165115661297497832588996765569056129E-77
+9.153892379347139E-155,1.6125604504198674E-170,9.5675975978022509629572170050415018719647218508794E-78
+1.2525495835384182E-154,1.3621678979573062E-170,1.1191736163520020911466325439916469769229813021547E-77
+9.099017928576115E-155,1.0983507386797687E-170,9.5388772549897696072882598954740624439099509101488E-78
+1.1427119971364318E-154,1.5225239656703535E-170,1.0689770797993902509491105556623861991285195554327E-77
+9.33912617898551E-155,8.587310833794183E-171,9.6639154481946454201247021022049524074627705164327E-78
+9.000657785921221E-155,8.881368759489528E-171,9.4871796577914671960338620415177044421384088594334E-78
+1.2114967647551104E-154,9.004675111976964E-171,1.1006801373492257445310608249850465252416822564648E-77
+1.4548788621378363E-154,1.6406719233830978E-170,1.2061835938769174329597941202157242719296338198281E-77
+8.913227797324613E-155,1.049267952688394E-170,9.4409892475972103890453428548566051237572697624265E-78
+1.2592535087312353E-154,9.401338183320052E-171,1.1221646531286019002033679870108075635263813322898E-77
+8.468956225861598E-155,1.083784223384484E-170,9.2026932068072327528828443963377451281446962365042E-78
+1.419046123863641E-154,1.0396369730329598E-170,1.1912372240085687608125074984545034725339557885691E-77
+8.81597295016618E-155,1.4271824389766333E-170,9.3893412709125562145297141323849642712996718733948E-78
+1.1143788114072641E-154,1.0549127776885574E-170,1.0556414217940030347764390368221660318061443333966E-77
+1.3979146210385844E-154,1.6427997392262177E-170,1.1823343947625749919192217627224512038311413895978E-77
+8.736703987902772E-155,9.042057191608501E-171,9.3470337476136100223662513862638700981568719793044E-78
+1.453001819160597E-154,8.999315466052413E-171,1.2054052510092185267602039069968577504374751988778E-77
+1.2465410242221635E-154,1.0769829393883866E-170,1.1164860161337281945799680863451619037924131922601E-77
+1.2562028180265761E-154,9.800216721201156E-171,1.1208045405094396768196318938168235467874099231152E-77
+8.320823988261762E-155,1.5761991729218005E-170,9.1218550680559286941779931623411856935041150844099E-78
+1.138265250907995E-154,8.297547653640276E-171,1.0668951452265564854911992435811810595291905674414E-77
+9.822284041415692E-155,1.3618713251737694E-170,9.9107436862304603372109090742370569406714084077918E-78
+1.3446285922212903E-154,1.0070275418117574E-170,1.1595812141550458877767724049550182577725219757884E-77
+1.2851853988968196E-154,1.5988228616424916E-170,1.1336601778737840891174895272032625502223321690719E-77
+1.364841584976872E-154,1.016898025035476E-170,1.1682643472163618861777637187229272936073808781878E-77
+9.438272275175301E-155,1.177279078153859E-170,9.7150770841899666938895573854891971480256987947119E-78
+1.0924358181664752E-154,1.4793575805018945E-170,1.0451965452327496571573921099521194850224102661588E-77
+1.2973161923574744E-154,1.1770256183295557E-170,1.1389978895316156299842987934769022463914116435301E-77
+1.0534119740681579E-154,1.0116964618802308E-170,1.0263585991592597427175373949870052393417656194542E-77
+1.367224085869104E-154,1.5065979640335597E-170,1.1692835780378958540604999612944291774674753605690E-77
+1.4585351829225748E-154,1.209257222145916E-170,1.2076982996272599225405059570047193374823095315548E-77
+1.0074138929608286E-154,1.1372976251601946E-170,1.0037001011063158884670625670138102886820271850161E-77
+1.3303884285985495E-154,1.050751190522926E-170,1.1534246523282522583320162650883005019950200154377E-77
+9.428536110870352E-155,1.4458828411108122E-170,9.7100649384390592398881459552099203474924800753674E-78
+1.3490693301404847E-154,1.592286602757969E-170,1.1614944382735910837465004815962879992288748606822E-77
+1.0530305059727827E-154,1.2056526724533533E-170,1.0261727466527177259099537188219301421421078540792E-77
+1.344040031256188E-154,1.3442257664963292E-170,1.1593274046860913406501038103916920818273281321923E-77
+9.7141907599772E-155,9.011536221542029E-171,9.8560594356858462263993990303500590385946737587303E-78
+7.738026086541751E-155,1.021067342218455E-170,8.7966050761312186693069966637874786673413265595989E-78
+1.116193390843248E-154,1.2610229878920833E-170,1.0565005399162122933439821804817052213446132419490E-77
+8.504569377558708E-155,9.748705791343026E-171,9.2220222172572913239976695614335654030029430397261E-78
+9.432249031555578E-155,1.2451028038711002E-170,9.7119766430709559445919130411081127054467845690078E-78
+1.0715617987643776E-154,1.218639594084034E-170,1.0351626919302964085827912023123637467876586997902E-77
+1.1085200049521729E-154,1.4271042973106764E-170,1.0528627664383298053252746753853280977285393396348E-77
+9.032495794212868E-155,1.5841510219508744E-170,9.5039443360180037614340551569679970732342885273218E-78
+1.2804618498062673E-154,1.4202066650337116E-170,1.1315749421961708736931582898142632829123072382986E-77
+1.2377932716041148E-154,1.1403976702985735E-170,1.1125615810390519215898557517030585508955208495139E-77
+1.0716560876807304E-154,1.3035650669864853E-170,1.0352082339706975732290643208778258652188526220123E-77
+1.0482086387948918E-154,1.117808766167251E-170,1.0238206086980725884482235450423091833404217646215E-77
+1.3105994406924702E-154,1.3324597861323893E-170,1.1448141511583748371029106199328250303200853208793E-77
+8.290297123459354E-155,9.631617558805884E-171,9.1051068766156473296795972138352849000043437524767E-78
+1.008399791062125E-154,1.2638452984470057E-170,1.0041911128177370892909414149790475599518927573850E-77
+1.4359505499502106E-154,1.1491347141699542E-170,1.1983115412738920457844159545755379944930197397985E-77
+1.2471989736739068E-154,1.0267885793127713E-170,1.1167806291630898879691344072945451284574261535613E-77
+1.2857880532597049E-154,9.709900082953763E-171,1.1339259469911185433422067025861844768136944402432E-77
+1.4629062950412121E-154,1.1503046668875463E-170,1.2095066329050090048868964921445053531937250947187E-77
+1.273655260669469E-154,1.3387652442875445E-170,1.1285633613889248766168982299352874517309261970871E-77
+1.1737539343233737E-154,1.0199561515368037E-170,1.0833992497336214423472665073350846205105202303024E-77
+1.4403695580690655E-154,1.2693010549399941E-170,1.2001539726506202200505235311675133199550422122029E-77
+7.726754716930248E-155,9.010790909056914E-171,8.7901960825286766023494866129366137326456969490030E-78
+1.0313301648554387E-154,8.40746004442883E-171,1.0155442702587803670447134433006382254715466033168E-77
+9.724607447933018E-155,1.1443391586199877E-170,9.8613424278507938166550139023294403902274083312930E-78
+8.882757591525283E-155,1.3610231411671515E-170,9.4248382434529265543663736827275738566814098600567E-78
+1.069505249510688E-154,8.914415023136951E-171,1.0341688689525942724716157652922002067873654394177E-77
+9.751939218218122E-155,1.2605886625071894E-170,9.8751907415594374744011028903281440795086239305535E-78
+1.1209283272474439E-154,1.055290205402795E-170,1.0587390269785297653906059266349976035219338104908E-77
+8.810631866460177E-155,1.0895379258089987E-170,9.3864966129329522448699385446108997087478311925026E-78
+9.271215351562194E-155,1.62653042467455E-170,9.6287150500792137212804783963846311853660512493250E-78
+1.0320690865775097E-154,1.6455214243510765E-170,1.0159080108836182827753634784161956140255336354890E-77
+1.0744708076509681E-154,9.83477680642024E-171,1.0365668370399316387122052568541555064281621134732E-77
+1.1428403425914488E-154,1.2231826084589806E-170,1.0690371100160409684699459044240645450283613054681E-77
+1.2626332576768285E-154,1.384487583934205E-170,1.1236695500354312845142455464682996820158712873132E-77
+1.4190984790282324E-154,1.4710578540750084E-170,1.1912591989270146217302052979684891423127323690108E-77
+1.2556460970277533E-154,1.414385939431486E-170,1.1205561552317462859863880566116269893962065730983E-77
+1.2717341749073012E-154,1.1696739038476142E-170,1.1277119201761154199406056576433183901656928024136E-77
+1.0097284862291027E-154,1.5898395401129579E-170,1.0048524698825708010164068881722656498870900794456E-77
+7.598779009076208E-155,1.5502061575348389E-170,8.7170975726305883288894581083068382940495529453332E-78
+7.571352402655865E-155,1.3397592428677005E-170,8.7013518505206225793652684655177422163063836825471E-78
+1.2986723911702183E-154,1.6392843838160441E-170,1.1395930813980130707049941078461797276763712371726E-77
+1.3987176998101252E-154,9.867220405371341E-171,1.1826739617536716813266834266537138480849319229939E-77
+1.4033939718170612E-154,1.5725634037236292E-170,1.1846493033033283799703900253541567831338767360193E-77
+9.825054007894058E-155,1.127348984390532E-170,9.9121410441407960972348543318973997103364258230630E-78
+8.137544091877273E-155,1.4129904120171801E-170,9.0208337152822381671457798034892394613269223925843E-78
+1.0981085617754791E-154,1.5952599577485201E-170,1.0479067524238400837439160666051973465169187087638E-77
+7.866700600386737E-155,1.3414743048123572E-170,8.8694422600221817057349762977738632330134527113141E-78
+1.3827117527850103E-154,1.2367426385942075E-170,1.1758876446263947635681012922147031498762920411754E-77
+1.0667345189082645E-154,1.1607046756779402E-170,1.0328284072914845279711210282283033195493269131074E-77
+1.3539617853167071E-154,1.179970976519076E-170,1.1635986358348428454290726475888265434686491483433E-77
+1.186530758245655E-154,1.4644994263958342E-170,1.0892799264861421235091248791899208765090136640312E-77
diff --git a/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt0.csv b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt0.csv
new file mode 100644
index 00000000..65c62d0a
--- /dev/null
+++ b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt0.csv
@@ -0,0 +1,537 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# High-precision sqrt test data for (x, xx).sqrt().
+# Computed from uniform values in the range [2^e, 2^(e+1)) using JDK JShell (Version 17.0.6):
+#
+# static void sqrtData(long exp) {
+#     MathContext mc = new MathContext(50);
+#     // Target unbiased exponent
+#     exp += 1023L;
+#     for (int i = 0; i < 500; i++) {
+#         // Combine an unbiased exponent with random 52 bit mantissa
+#         double x = Double.longBitsToDouble(
+#             (exp << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         // Combine an unbiased exponent shifted 2^53 with random 52 bit mantissa
+#         double xx = Double.longBitsToDouble(
+#             ((exp - 53) << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         System.out.printf("%s,%s,%s%n", x, xx, new BigDecimal(x).add(new BigDecimal(xx)).sqrt(mc));
+#     }
+# }
+# sqrtData(0)
+#
+# Note: The value (x, xx) may not be normalized. However these can be added exactly
+# using two-sum to create the double-double number before calling sqrt.
+
+1.1246341619674243,2.0633413041790255E-16,1.0604877000547552221580767435194336942236028365791
+1.3845965151175854,2.2154874351057569E-16,1.1766887928069960312973242462508474246193389174965
+1.8394952867616208,1.253270190104531E-16,1.3562799440976854978075178092336639056093100162170
+1.0717136584482534,2.0313510911002419E-16,1.0352360399678198971135224400324602695253542367923
+1.8948283041701461,1.5031360022970026E-16,1.3765276256472829279810261463448267604345549675461
+1.0771936750481892,1.9945589333197592E-16,1.0378794125755599510515818442547805304967507244499
+1.9782613228290837,2.0688253463835318E-16,1.4065067802286215576086196894534323782599558579959
+1.936864516660365,1.5314090675991446E-16,1.3917127996322967028352056986433202143754963373564
+1.8891066631519138,1.8483888312079605E-16,1.3744477666146189214589093212690490905645258802134
+1.2414404960792846,1.7050179951253537E-16,1.1141994866626374502364381186527143925631696215942
+1.430158184128644,1.801064491159652E-16,1.1958922125880091656603225241003924220204539998425
+1.0051699095130904,2.0350061688162322E-16,1.0025816223695158973381153068700409911524367519702
+1.4907194212711827,2.091679028742676E-16,1.2209502124456930291614774973118880792226101837049
+1.662871670065853,2.2077310507466795E-16,1.2895238152379556710125997214018763592826042023295
+1.5498583273580393,1.3807130057608686E-16,1.2449330613964910838712576820320373276080081268811
+1.4043374512344369,1.57176181766318E-16,1.1850474468283694126804927122976429404731722289011
+1.4155891275848727,1.6768134497192488E-16,1.1897853283617481306183659746562449492245557486511
+1.1904214573260279,2.0767400052122845E-16,1.0910643690113008613062353675369442508630476801702
+1.0446353265050536,1.961222837742887E-16,1.0220740318123016654587057922276435819208934519933
+1.01795277962904,1.8257142176572538E-16,1.0089364596589024761313197552392542257982541004143
+1.886559697916487,1.9062060265421833E-16,1.3735209128063857449913149166120863004436680390887
+1.5109532268871275,2.130182140847778E-16,1.2292083740713483008278507548872787302665385425255
+1.6915938837169766,1.5003820589361377E-16,1.3006128877252357749739761122408148912542439783767
+1.8375089386100762,1.5594903298333791E-16,1.3555474682245828324447354120183621912582034473086
+1.4183857412540133,1.7281390044407564E-16,1.1909600082513322835360294680700314307518594185351
+1.6102370014399565,2.1120247504253527E-16,1.2689511422588171215523911177012226410747700212382
+1.4930153457641333,2.0489482782467866E-16,1.2218900710637326030766133091189785811549485056258
+1.3855818701312088,1.9568201493634907E-16,1.1771074165645245013793868147594338690509205552357
+1.7067557465839918,1.6796435141867276E-16,1.3064286228432045226271785033831725578326948099290
+1.470861465945361,1.4349466559158887E-16,1.2127907758328973532297071739726831485765923399679
+1.085490593227568,1.276622343341253E-16,1.0418687984710781956988508627673936467521928848582
+1.6577068420328918,1.7559106286895815E-16,1.2875196472415059555021811745127694756223659356494
+1.2890459539634511,1.688486316062213E-16,1.1353615961284983007420952889443125417603249158910
+1.1850557923070535,1.3113065842613252E-16,1.0886026788075866303140387535230306993772042452846
+1.8814479835125413,2.0340799372664366E-16,1.3716588437044181727240742369184210954941107633031
+1.6430490746501107,1.1268787366092062E-16,1.2818147583212290562062405129767517902152852881274
+1.2301691703198154,2.1946172223809254E-16,1.1091299158889438457147784607524662236438581112034
+1.839859543346691,2.067898440390766E-16,1.3564142226276939600061146328049418917099858245870
+1.5833592607344296,2.2165458092868598E-16,1.2583160416741216229556045050013626360555578349051
+1.0871603008021296,1.8723160982258032E-16,1.0426697947107366649479170264383891253313781741135
+1.2959614943506872,1.9127829396866094E-16,1.1384030456524118876482828630293436671037804837070
+1.6124704027097772,1.1212972921589702E-16,1.2698308559449078306791518165491758345258574111284
+1.5555549020053896,1.9221766021676118E-16,1.2472188669216761760180922796562456055421148640768
+1.4836762480390364,1.9407930103863544E-16,1.2180624975915794979069856772615345156084240462651
+1.000857414857662,1.281647996064243E-16,1.0004286155731763338372764967221165649469878435112
+1.9046178256460216,1.2654264140596483E-16,1.3800789200788560999949069927024512313155595284222
+1.1765136210437583,1.4411867317442959E-16,1.0846721260564219384389272376529834453619985144655
+1.8578968707655603,1.1247506449051537E-16,1.3630469070305542598958913750512701181206915903963
+1.22595894357192,1.746162719051968E-16,1.1072303028602135440922294712782992568147502189732
+1.3758637690257802,1.4751651922264243E-16,1.1729721944810884446268477068078013087571295081138
+1.9008119977551678,1.16720484593825E-16,1.3786993862895449787154223480980124335423723808087
+1.4578648591767944,1.2237601112838948E-16,1.2074207465406557887029214488968557218328223737584
+1.078869870038431,1.974414622948884E-16,1.0386866081924957268218279032639825155957507002325
+1.4363309452273494,2.1236640069123751E-16,1.1984702521244945775632473789609331429372799562640
+1.3744712926249165,1.7541823856755547E-16,1.1723784766980826375677603519091748255059077948135
+1.0098649542989235,1.681118535184268E-16,1.0049203721185692541737565960309893314933934562711
+1.1649292455087532,1.2641733362432549E-16,1.0793188803633304990514964078967123768192975983153
+1.1586493287980535,1.8520134100360974E-16,1.0764057454315513247958628852693260674901092937974
+1.6637872277967196,1.6567097231048961E-16,1.2898787647669527318018713758883982669049271532384
+1.208789461582541,1.7750473472600766E-16,1.0994496175735117089984781932277151542196048685742
+1.1328087910780442,1.662950705271531E-16,1.0643349055058019591992314094318583764975826148936
+1.6651458908305603,1.5887024531094637E-16,1.2904053203666514887520768999826458326184894122741
+1.4344507470222871,1.4657338087181805E-16,1.1976855793664242366333891951814757132547032934697
+1.061634005070457,1.4593803957025808E-16,1.0303562515317005906372772840545210117601228720685
+1.9321666091225904,1.76459576182412E-16,1.3900239599095371685569314874986236809652695004536
+1.4096032246328745,2.10055101635116E-16,1.1872671243797137471109177760569519823447263536017
+1.4977350746922626,1.1956018755427457E-16,1.2238198701983322329885958821875127147125894326561
+1.107182960925695,1.479181714579895E-16,1.0522276184009308978081264143221335090719805555080
+1.3128633447641636,1.8034627750096403E-16,1.1458024894213503731549545072388573144927462259497
+1.8934704410291194,1.558720119594482E-16,1.3760343168064957779794539717193483979614969770469
+1.0420506540410588,1.8139259380315384E-16,1.0208088234537645146855820930006837289163963641996
+1.3726192001271567,1.9128680192438171E-16,1.1715883236560344147860035853515188689577049408318
+1.1140770042669343,2.2069007645653727E-16,1.0554984624654526544635389047379396306145914411391
+1.1728593407387022,1.907499761689392E-16,1.0829863068103411755308860591256289200268506840832
+1.8159213476819982,1.2586502397029175E-16,1.3475612593429652038962699720831377746494507056822
+1.6104564259314864,1.7134740340907834E-16,1.2690375983127870211454236965566636272213672134184
+1.1358056665921714,1.5315406019705557E-16,1.0657418386232997879780653828948007017549108574906
+1.174337491549703,1.954607349114811E-16,1.0836685339852325438393038600004679604004896286837
+1.9584300773253958,1.3322631458684773E-16,1.3994392010106748079785994170463406813017258278651
+1.4528975558385873,1.87188464131773E-16,1.2053620019888578890130123409216556226225678004700
+1.8792449380805596,1.1315983239385167E-16,1.3708555496771203222278226945740527564916136690439
+1.775500938692934,2.001474974316786E-16,1.3324792451265175731135667643519954252341184783563
+1.225240691241955,1.2991024750239343E-16,1.1069059089380429463172739912729692631722996550649
+1.6018262936274852,2.1371493291504186E-16,1.2656327641253150652423095451355061550961220205055
+1.2467141907919725,1.3042552235672025E-16,1.1165635632564644429212080744441492032523792278789
+1.746527791766017,1.6982426248059275E-16,1.3215626325551192288119652191507788877803950781575
+1.3449840646602202,1.6992157963044805E-16,1.1597344802411543443920911292546164354942597575622
+1.5957388833430064,1.6398964370140042E-16,1.2632255868778967073292448209084332949943898275606
+1.856401676182631,1.6100570120784336E-16,1.3624983215338766528339375491947768522783801718967
+1.7474537162668524,1.681008294398367E-16,1.3219129004086663301397654469171263281857872058715
+1.7659822559957858,1.405033344788821E-16,1.3289026510605605620612182414226259693843454804758
+1.7298758364628828,1.273416944324261E-16,1.3152474430550636562053904482052761789379879234657
+1.062148454259941,1.7379477575786736E-16,1.0306058675652595284376359593489777866757352191327
+1.86174786176402,2.0663837543932262E-16,1.3644588164411633017503127250370141123796921033969
+1.8045967616181458,1.8575350015312823E-16,1.3433528060856336098676027249679828211276602887138
+1.8129766670799248,1.6295599729605012E-16,1.3464682198551605392251360641460712754700249154177
+1.9036698935270906,1.6439406705672856E-16,1.3797354433104524548116009759998108323281827778425
+1.22809832478078,1.3459055135247048E-16,1.1081959776053963345496690750240911470858261105509
+1.516965091241133,1.3176708754958585E-16,1.2316513675716570737193463643483692348235647664907
+1.6952896777596214,1.2365497417646845E-16,1.3020329019497247268167350739314852706128878153799
+1.5506534920096127,1.5490424687304504E-16,1.2452523808488032042003219064262806250977679983187
+1.1764025701703646,2.1693414130616904E-16,1.0846209338613951279820041253407044771183903803848
+1.554748642270848,1.9653721479579433E-16,1.2468956019935463212712901225994517739917472863860
+1.3864269190809395,1.8733324512536147E-16,1.1774663133529297278948523968198552300086348842398
+1.8978521439244593,1.9487659628644406E-16,1.3776255456126165812884538751342671687772440870551
+1.1435845620687977,2.154980894573856E-16,1.0693851327135597860156455267661049489440261856409
+1.9468195423303831,1.1771930564757567E-16,1.3952847531347797399896153495186108890985860319648
+1.1878397002471361,2.1221618258076552E-16,1.0898805899029197572117961977972543896848185531583
+1.244430283330443,1.467985941278487E-16,1.1155403548641542438962502574505878309362517056885
+1.3545826990720735,1.6717295363610138E-16,1.1638654127827983883639003909939225713306666623227
+1.6565058942943849,1.2380459173776322E-16,1.2870531823877306306000973006239577319674869363582
+1.3575229305078598,1.5236405961950663E-16,1.1651278601543522970685843523697798575796576573201
+1.0185265372801582,1.2218317756526019E-16,1.0092207574560475250872502650184711332551719617260
+1.1261961397224411,1.3549620556871034E-16,1.0612238876516308812997470992351300142777230154985
+1.813950917986658,1.3714653280185578E-16,1.3468299513994549232980168609973718176423499467145
+1.8764465750973969,2.0587642120551047E-16,1.3698345064632432012576864360782583107621675946516
+1.0792066600570178,2.192759002482375E-16,1.0388487185615709457703808371037476883654062784959
+1.7558295649963305,1.1428568017226776E-16,1.3250771920897025937748790839032516217574296537787
+1.161840252768994,1.8551913491012598E-16,1.0778869387690872875129500469146133809342207343218
+1.0481209589338165,1.942627777218126E-16,1.0237777878689382416126824522563735248732798079604
+1.7572466956622081,1.3291808814437918E-16,1.3256118193733066892071755143968287089100319049495
+1.088051765726724,1.7670948459474308E-16,1.0430971985997872883197083009481874263369649488918
+1.2289295310713144,2.102291957623461E-16,1.1085709409285968778969742691233107027794311651080
+1.2088996756805286,1.8880339412695034E-16,1.0994997388269488686952940560489558740573688730493
+1.3866296040798363,1.8747214364848899E-16,1.1775523784867645758148681721011401838464632944392
+1.6026372609488102,2.0949173780978182E-16,1.2659531037715458812317415385533437776901880611310
+1.9828928071694432,2.1510441357641557E-16,1.4081522670398408928914580690505384047344956213995
+1.8237218179148837,1.8401436376155985E-16,1.3504524493349937758667756691412490656594682083383
+1.761883646820361,1.1183834066133172E-16,1.3273596524003436151887870190230379435007879984689
+1.4559927413530733,2.0987707613582035E-16,1.2066452425435876790789496706324595277521896390359
+1.8805888968270845,1.5261533453400027E-16,1.3713456518424101068415621148173612488389944570888
+1.3788647194681727,2.0162671598549818E-16,1.1742507055429742791081368104665251712295035465782
+1.410695089652483,1.9105906739019447E-16,1.1877268581843568027903808694851240376748723608406
+1.2879325715056666,1.692216995851276E-16,1.1348711695631653567941002495491036915496290103098
+1.7141090202830402,1.3989587445524707E-16,1.3092398635403064983453318526725358973933382515029
+1.9588486438896844,1.266260310871368E-16,1.3995887409841808629653578127893943817715874430342
+1.0004625872218926,1.6646094462546472E-16,1.0002312668687641040964345417103372882568351881310
+1.8981370627422363,1.3812639976860483E-16,1.3777289511156526453748888213707486021227712801905
+1.624575342226724,1.5667326889870752E-16,1.2745883030322866207948611329172100618004020764466
+1.975634410143893,1.8501566888925457E-16,1.4055726271324058285657097771675745967251224248331
+1.791274709053149,2.1986483211248328E-16,1.3383851123847534469688247975110014317032610981785
+1.9290549448216148,1.8166585438755478E-16,1.3889042244955607274828788317319643135878705466086
+1.5047655216185563,1.503039209278214E-16,1.2266888446621483835802706303980157050973102248416
+1.1389666102259606,2.1847382732374982E-16,1.0672237863850115632282197221987933922144037102746
+1.1262637269641078,1.2834027043468704E-16,1.0612557311808063646924315736968899744992100282184
+1.3842070379884843,2.1373697174085558E-16,1.1765232840825907501338431680921152752327834641891
+1.6470233567653867,1.4641585195635217E-16,1.2833640780251669638898426120606226964717027884322
+1.1105367585953565,1.3583684396002187E-16,1.0538200788537655467622644435043349926971090984608
+1.0517207996657914,1.1588711389745812E-16,1.0255343971148853992741142395489173405635635710497
+1.838232416191359,2.1871656049306872E-16,1.3558143000394114587204575496984911735982876858558
+1.8295469502160606,1.2946711086835931E-16,1.3526074634630923654941927196181566796174154865193
+1.0601343031431116,1.174097107421901E-16,1.0296282354049503003196659737495890557677440887343
+1.3601868725243111,1.1119870195811217E-16,1.1662704971507730172373555338873800700146349748779
+1.6354652203968416,1.1318969085333587E-16,1.2788530878865022021996736483994284898892208925144
+1.517201376481688,1.8932609477461324E-16,1.2317472859648152105161423383551291347834886640534
+1.1104838097592002,2.053529810165334E-16,1.0537949562221297953843176083312746096763968392361
+1.0304354760130134,2.1671182047231155E-16,1.0151036774699487104766800617217195871016348212964
+1.957475328825454,1.6553157668392631E-16,1.3990980411770485422906338377168829945833352990448
+1.5746815529458877,1.8153159157599985E-16,1.2548631610442183679385089762334554466047867294793
+1.8013100255875967,2.0216202659422544E-16,1.3421289154129706317589554430095632147713870911410
+1.284107005423619,1.9714721082851837E-16,1.1331844533983067286113016943295173697511995374759
+1.4898859548349168,1.547482121773516E-16,1.2206088459596370504473218052179137497557859531604
+1.3777008113501095,2.064008555385002E-16,1.1737550048243073084795955434627908085317312793347
+1.3292930045194868,1.600967785835472E-16,1.1529496973066461596454382388430271631007356331997
+1.5183241433618033,1.1997981425827606E-16,1.2322029635420470958432635833904010679970624817533
+1.497316519447668,1.3344292531931726E-16,1.2236488546342321547873834055265396359595962716634
+1.7000455645756065,2.024170380923698E-16,1.3038579541405600208279700938642033042576425370439
+1.8903419506133687,1.1217865002146343E-16,1.3748970690976720712414361268511840634307695278486
+1.3641404496784646,1.252604667939855E-16,1.1679642330475983887659133164580148977165194097380
+1.089793037717713,1.3194541497601113E-16,1.0439315292286717290537236756507900003443593880071
+1.0211197520677793,1.5815282521977746E-16,1.0105047016554546600535472866401492498773219137019
+1.4246755298134357,1.241084596774758E-16,1.1935977252883133020193995966419106971390578952994
+1.1942766436295489,1.2575381803859493E-16,1.0928296498675120636794142864503325747960344852644
+1.0468577798762877,1.6153530173968796E-16,1.0231606813576682095790918410427892304238875598651
+1.6070053004251528,2.0126324763025296E-16,1.2676771278307236907771187400827379451082681981611
+1.7350858100521223,1.907303061813261E-16,1.3172265598795533123150303341920477373925325232072
+1.608677949414446,1.501815532090163E-16,1.2683366861423059202737666391425942382161881498796
+1.0891094400843648,1.475411839548293E-16,1.0436040628918445237916834043003808752994977587764
+1.087036200798598,2.1430957097875144E-16,1.0426102823196202772378602843687726376474272025677
+1.0099859111405216,1.456554888570039E-16,1.0049805526180701940611948069461948776582735762220
+1.8516259658680538,1.3955095179902226E-16,1.3607446365384116315071054922841836692410173424914
+1.1390704391666395,1.8480861902629194E-16,1.0672724296854293380190766062367409553112750941536
+1.6267876862076407,1.5608708870809536E-16,1.2754558738771172592687565156141522880454211348432
+1.6019428250201717,1.521191873164621E-16,1.2656788000990503532310268697301454980429780476332
+1.3778129465861182,1.9069608570278263E-16,1.1738027715873388510353464428291344862303931347146
+1.9793083823271798,1.6024199305388173E-16,1.4068789508437390509259926337489063361146791947746
+1.3855286263214894,1.3379414444344886E-16,1.1770847999704564893515888922418341503104982578094
+1.430157658586211,1.897066007241628E-16,1.1958919928598114048722774202037609968923310208307
+1.6937424836922288,1.8071177744353855E-16,1.3014386207932470050952007177358821904857799170113
+1.9313026503492152,1.2733962841295095E-16,1.3897131539815025256916604656221766997867976582396
+1.255975385568395,1.9015263430340346E-16,1.1207030764517402908062860764677688910436464478415
+1.9106659159537818,1.605863027725154E-16,1.3822683950498839097923360154360328163833834006718
+1.267850267687038,1.2784834547206265E-16,1.1259885735153079876735818788651759519616268356727
+1.7484591222076629,2.1182445405304915E-16,1.3222931302126858873497131159756305268951759074212
+1.1008455905471213,1.9881662026868648E-16,1.0492118902047962764008300584909384620512096375864
+1.4315292268702837,1.1271106044712534E-16,1.1964653053349619943846128700501106217419874517599
+1.7740785911813972,2.0921507037617064E-16,1.3319454159917355345741507451155749362280683326642
+1.4418154855098682,2.1561470449607445E-16,1.2007562140209262190432896148693943032205862321905
+1.6445738083584762,1.2128736812881247E-16,1.2824093762751722567874689218369362996994176031899
+1.5973057153221768,1.5781323155462503E-16,1.2638456058087858691589299072165951670353385216450
+1.909513155723589,1.4613971476538026E-16,1.3818513508057185651955825560560276448338841124755
+1.1481824563746177,2.076890987773826E-16,1.0715327602899586083397717111243668897903671513186
+1.5402369154230529,1.9473792063038628E-16,1.2410628168723181312006395868619580783911053527465
+1.603920827186901,2.2151298679432165E-16,1.2664599587775766744731285368514349989037821166522
+1.1740104522709134,2.04160910572808E-16,1.0835176289617597122288951701885668948161475674389
+1.058439654126125,1.974822035123449E-16,1.0288049640850908864957537420170959302710931558920
+1.9131605807181706,1.954619557942885E-16,1.3831704814368223167152547939272751131284778851886
+1.5687039095007829,1.3478893557148603E-16,1.2524791054148500252573441419844463031045760421151
+1.12664805692347,1.9782448449159582E-16,1.0614367889438683994690449373120958890990241063776
+1.4437317901254085,2.1145223541490843E-16,1.2015539064583863867711028187364075403692958411857
+1.2386167689543583,2.1157945118713348E-16,1.1129316101874178327830370441265118770642189597377
+1.974077611962088,2.215752169346452E-16,1.4050187229934297912173016575752489962572567945934
+1.182551814013697,1.580350609804712E-16,1.0874519823944858580023775696744906048669421250354
+1.7754492762362513,1.915007400192857E-16,1.3324598591463276995914737909129613633773337952631
+1.0756534215641562,2.00933202778769E-16,1.0371371276567802155806665793169737413914350893204
+1.0740613404815564,1.5894899917570384E-16,1.0363693069951254571686137648466758018041057147246
+1.0517690481609518,1.7420869052059159E-16,1.0255579204320700000831343482270870931009730411955
+1.8366774902026388,1.568015786481695E-16,1.3552407499048421747611654279481145725500607959112
+1.939439130846044,2.0247607141290065E-16,1.3926374728715453157224818701588912869020918773884
+1.6041861559421573,1.7982911363293762E-16,1.2665647065752927725411464085315873180587268255182
+1.9344151598875818,2.1367601496207525E-16,1.3908325420005033985624991576767429311349968772076
+1.5364040163701294,1.5244564059500575E-16,1.2395176547230497222989053628095435071336482752662
+1.207523617510055,1.3356788956724077E-16,1.0988737950784226091945612324478833095068535857471
+1.6141930672992715,1.8600479064500982E-16,1.2705089796216599995156671228758197280430366924471
+1.559323189621033,1.5977790863427316E-16,1.2487286292950254329693668340413356278668078157805
+1.5576428866931424,1.4240279853045924E-16,1.2480556424667702206549686621147095694605941001103
+1.6538097635087505,1.6884575687470321E-16,1.2860053512753166995596261017350984568848865010494
+1.0134283419184205,1.8018287910836906E-16,1.0066917809927826371522625605911336045248171011651
+1.6617979825930136,1.860002314387086E-16,1.2891074364043571739093356153424993008029785916256
+1.6589367294551751,1.830371662909273E-16,1.2879971775804383732475787875807286077439769009892
+1.1001308103034004,1.1557251128538057E-16,1.0488712076815725739221624932677236110508963073153
+1.8949841396223597,1.4706314115283696E-16,1.3765842290329930344319961318843213048891908545559
+1.8922884409782943,1.1546207510047433E-16,1.3756047546364087827197744113243030627509367630381
+1.9109071550706065,2.179408740520123E-16,1.3823556543345155742613447163825277564693595772901
+1.5948575404244876,1.800110756828994E-16,1.2628766924860430546080150538148820605426723713781
+1.586121601377602,2.070826352398365E-16,1.2594131972381432290299063572221295341351136083547
+1.7288487604798455,2.1204564302721791E-16,1.3148569353659148924814043976472862580358703526873
+1.0596489471451083,1.9360917036712655E-16,1.0293925136434150598147780304786905258299140049089
+1.6383652293958058,2.1766947211182937E-16,1.2799864176606742311090762110706946745824691595970
+1.7710722758102082,2.2185945869239848E-16,1.3308163944775434082660142819798542493862044532078
+1.8517135028739276,1.1183555728087726E-16,1.3607768012697481551901151640713430521722500524302
+1.5970765108351925,1.8902268376594956E-16,1.2637549251477490166818799106497566211305492864860
+1.6863540260402978,1.954777255771731E-16,1.2985969451836462802655884522619620616039520724056
+1.9586550554164737,1.926763758731677E-16,1.3995195802190385430977283066499067952473628752394
+1.263832707677168,1.2694247874892184E-16,1.1242031434207823680085650807514256801771533122703
+1.2755090030350376,1.8046164485226161E-16,1.1293843469054446475151703678794101695085709819405
+1.4292164189368224,1.9088983557062748E-16,1.1954983977140339842997711838172393441985685175198
+1.214823676749955,1.72840649829082E-16,1.1021903994999934326358337106109090476809972911609
+1.0063929737921344,2.0757811205438185E-16,1.0031913943969688206060653517123875890722253040138
+1.0268129104064008,1.3068984777076451E-16,1.0133177736556291649899661115403677695084617024157
+1.2177645360262526,1.391883358304593E-16,1.1035236907408253296612476728373757376728349795524
+1.325528994022949,2.1396625655410064E-16,1.1513162007124495062630268394404380107009247736430
+1.6381329856012063,1.175771489445623E-16,1.2798956932505111238412650246383501272731340531962
+1.2469292704604524,1.586288254637069E-16,1.1166598723248062481502581386205492887692184107937
+1.7511174303817443,1.853421919895162E-16,1.3232979371183741337593429647094168941498554569501
+1.279228230318293,1.6780666330111852E-16,1.1310297212356062049440842737421811656063022061256
+1.3652319715510408,1.4113263527110224E-16,1.1684314149966359723464139486762231038957950031806
+1.0640634075876492,1.6724792857368038E-16,1.0315344917101169503940172262365139221922373421287
+1.0830701460164485,1.98180342586638E-16,1.0407065609558001932566362505787606857717458761273
+1.893557157867758,1.553570718309107E-16,1.3760658261390543184708726119323169883269232616176
+1.7787406062341713,1.3535853632018016E-16,1.3336943451309117066409180695018945534653532035955
+1.6056530162397868,1.4941864841931892E-16,1.2671436446748202015582923472695746672268894445055
+1.1313141664600652,2.0089602996523433E-16,1.0636325335660175353366517950536595692552148959486
+1.5491803661040535,1.582018210592353E-16,1.2446607433771074299875344424824653865425718952433
+1.8335665130840615,1.8001697148173846E-16,1.3540925053644088716853712873995785836439292637226
+1.543952676753255,1.2538966452807766E-16,1.2425589228496389931811176953839580085154087358290
+1.6406383702861291,1.4019411804287175E-16,1.2808740649595998721888621906807042185741738513010
+1.0445723829930353,1.5627938094487714E-16,1.0220432392971617805084736481169017152950955809368
+1.9730996204573168,1.1555894428612995E-16,1.4046706448336267946150139434706342536117397558610
+1.935815113327321,1.8732697752645648E-16,1.3913357299111243307394822646726178866407212077698
+1.9689946442834596,1.222101253150837E-16,1.4032086959121439707096759564938125578920734349052
+1.4859992350151794,2.1965520389422505E-16,1.2190156828421772223443462906402598348648163633705
+1.5155733307768757,1.7218089894032214E-16,1.2310862401866393612284048866158684754710835252628
+1.79065370917099,2.0407229904078543E-16,1.3381530963125968817560053477381881288420557964121
+1.7000635329214768,2.0799190067560972E-16,1.3038648445761075261083182214324108832860823288174
+1.691547813819474,1.5873346521959596E-16,1.3005951767631133665355467739753008530561116749981
+1.785117433205102,1.4990324446044095E-16,1.3360828691384012455849754206705879810340294442109
+1.0307995389127829,1.9829816559641545E-16,1.0152829846465383676548033452050538949825033423550
+1.396781721811154,1.6845963637879966E-16,1.1818552034031724723295000337678512034394159069873
+1.1240426644290102,1.389774273563197E-16,1.0602087834143850271841443669581445147701211394788
+1.4207695893023573,1.6265101628043956E-16,1.1919603975394306144181464600139915676740081582234
+1.4558024741052302,1.8431921389757103E-16,1.2065663985480576985457281404758539703282055357598
+1.522526634746972,1.663166046698431E-16,1.2339070608222371643730680826182908145495395761446
+1.6666146650475933,1.3150479138938383E-16,1.2909743084382405281410535914573235491320954744585
+1.838886465962315,1.4785976501863264E-16,1.3560554804145423029556938824493585140393448682243
+1.6927875472812048,2.1139917549380693E-16,1.3010716918299333485589500276812063414999751744133
+1.073958399524276,2.0712072704808825E-16,1.0363196415798922230324225031744357015973672019729
+1.4621986714803736,1.592093898457918E-16,1.2092140718170516701984601134763981803254745878961
+1.7527042028080664,1.528844413150657E-16,1.3238973535769555776208552104041904368166273292135
+1.039653928923371,1.6724760790686706E-16,1.0196342132958128717679012002085979030704503086581
+1.003302259220032,1.193663143098942E-16,1.0016497687415657470754418197652035684663812974506
+1.965251999120478,1.7343716090576083E-16,1.4018744591155365512434386453532724935128379357617
+1.1535961048822385,1.48730933691079E-16,1.0740559132942002774429191299122571974197971784695
+1.8777106435313968,2.0762178547352984E-16,1.3702958233649393399991650588409917471472016436727
+1.0679709423253227,1.7620889339302539E-16,1.0334267958231598532872602285754208893763498072259
+1.838655037098687,1.366149295027942E-16,1.3559701460941856135107969923172353445277491836784
+1.4690121134808904,1.5515987446100935E-16,1.2120280992950990993938871873615443942288073097620
+1.828320671327293,1.9581623399204598E-16,1.3521540856453058952894262310657942955054294647283
+1.7161264255746023,1.473465925177255E-16,1.3100100860583487938500330123143687566623081327486
+1.1661641544831178,1.6130719128695243E-16,1.0798908067407176483691640059290376145249226794843
+1.5567107952886567,2.0949258070054742E-16,1.2476821691795779427153434743271420844097030047202
+1.9065565076247406,2.0248478625827317E-16,1.3807811222727303808008951410108838685336124194069
+1.1725549228632945,2.1042054080601503E-16,1.0828457521102877544647798016770015352950437809674
+1.4052811072027505,1.2157498328155693E-16,1.1854455310990676548549708219748476712840040389675
+1.4356879422440216,1.1834343621321033E-16,1.1982019622100531968811133362607874457269067420619
+1.4490815386135498,1.3475865905702446E-16,1.2037780271352148082242007624919884896060809292665
+1.5255229599795188,2.104254673553918E-16,1.2351206256797426753714191958211549979844577098358
+1.1996302315067904,1.1289337163432488E-16,1.0952763265527062836137695063595304275382069147187
+1.8204909918491465,1.5017230783768082E-16,1.3492557177381708927519871246839160096912511074571
+1.5286849061129761,2.0107952986431148E-16,1.2363999782080944130748882691760522120729163692103
+1.0290059079979321,2.0122886488936788E-16,1.0143992843047220547927934474587818696615877187296
+1.8688738953113446,1.146900457094273E-16,1.3670676264586711142072367124992840974722438311832
+1.3337196777383293,1.6384998241157406E-16,1.1548678182971112206633435915206677252477093981775
+1.6961828102947125,1.5265928737443312E-16,1.3023758329663187780744087231459668508447871656299
+1.5178223153094044,2.0244428311208204E-16,1.2319993162779777171744964187458078427633376425585
+1.4437251635229877,1.4173199418281773E-16,1.2015511489416452985950941213659379016913693435157
+1.1761960637153253,1.3146318723162874E-16,1.0845257321591430957334882662245222842550913016302
+1.0911045715234307,1.2671698097485145E-16,1.0445595107620392453128556822890955303653451931079
+1.2233774685905516,1.477828567233771E-16,1.1060639532100084577142170052941205400064119706006
+1.9058761571574938,1.9647083042774414E-16,1.3805347359474494611428625233651629965497801222434
+1.2367960710864294,1.7946033963717245E-16,1.1121133355402360228915343744755260364221741086676
+1.4665901759825932,1.6348790437144014E-16,1.2110285611754139749634860633930454209272900750962
+1.0686801677651248,1.5000982673278808E-16,1.0337698814364466216917716007280562605335112429714
+1.1029399505607143,1.8864308968600992E-16,1.0502094793710036036512861676016666933305923079556
+1.0103142883397218,1.3992322653880052E-16,1.0051439142429913508344129208999195483797682962566
+1.3421780959889043,2.1351380081218493E-16,1.1585241024635199471278954734886863527864933302462
+1.5599217448504816,1.4359333451357626E-16,1.2489682721552544595560561154029300429495211144470
+1.292158404602216,2.0230313665939628E-16,1.1367314566784084097806482016561605487713582350127
+1.6675305251049026,1.9190429844774063E-16,1.2913289763282255178123970389821782867695476467313
+1.7137617475041518,1.9203227689170967E-16,1.3091072330042913523740751887336623454407564978782
+1.8329069209249735,1.8373279119678556E-16,1.3538489283982071095757481061503244578467092733769
+1.1472040509094976,1.5588668601558782E-16,1.0710761181678442105447642277208168869240053312466
+1.6598378324934442,1.1324422956275836E-16,1.2883469379376986359014804866080376072819236388638
+1.0762360317324278,1.6398588473918225E-16,1.0374179638566260777357835967530601235095853723615
+1.103296973468958,1.8010252352008474E-16,1.0503794426153617950912320508734495351338578814942
+1.3955682563495544,1.6669975152321559E-16,1.1813417187035910987016855015228006799676244481018
+1.4862156352841327,1.8012935748772222E-16,1.2191044398590847872043775897496202758858580594638
+1.8999758076585613,1.2759410077470016E-16,1.3783960996965137454376534594699690761946721731639
+1.5456094471949091,1.1498016632399985E-16,1.2432254209092208025255734226086825212408466036445
+1.8823520566770535,2.2133892281760542E-16,1.3719883587979359168746041512808052998360578499329
+1.1826358952196003,1.2841573615358737E-16,1.0874906414400081902340153562174794088501786211729
+1.791654593206946,2.204324844004383E-16,1.3385270237118660090877287010640605275630247346556
+1.0860912412009975,2.038061204003383E-16,1.0421570136985106625590719191469606242902859069811
+1.6622596876965423,1.790884907433464E-16,1.2892865033407208085780785255800090940779701046318
+1.5869890451860678,1.4276174119878275E-16,1.2597575342843034182065049946842055655950147184188
+1.6024347401235766,1.8018609789618889E-16,1.2658731137533401069311010953699916069443317375749
+1.4459512771765886,1.924422323005704E-16,1.2024771420599182220509253681969450125507480931620
+1.5828248062977222,2.1034621083019473E-16,1.2581036548304445505408931253258202610173837233857
+1.619044301186014,2.110735269983883E-16,1.2724167167976119715498382220431880189859504495481
+1.1921893924515006,1.218597360987629E-16,1.0918742567033534999678650775537658385567303550829
+1.894651247040949,1.9487998169269104E-16,1.3764633111859353473120030133862305505705391555611
+1.295907049708926,1.4868719147681257E-16,1.1383791326745787442945810556018529392743077675002
+1.7586507119619441,1.8560916113297586E-16,1.3261412865761869110332746992858217902454299572336
+1.285841867730352,1.9618327326750725E-16,1.1339496760131607465245258764091894009760414304790
+1.4510088032399413,1.336437655170708E-16,1.2045782677933142241952439078507778250846566358993
+1.7514492295138768,1.7838941300669727E-16,1.3234232994449950383918578965639509721344475230574
+1.3126851689188408,1.5231160927153422E-16,1.1457247352304308817332445659550948906832490227699
+1.6926235126801348,2.1230130522601154E-16,1.3010086520389228337210134316952742190843425224702
+1.4499076504787964,1.1571638946450115E-16,1.2041211112171385540552647611087493423378948306568
+1.9395339332907353,2.1710060615733116E-16,1.3926715094704621441213395189312146701441447451513
+1.119522748074611,1.5928093988321807E-16,1.0580750200598307343212154934768078616114844718941
+1.6864999112374606,2.093517371736603E-16,1.2986531142832025637816945888616829029445010779500
+1.2927518747057662,1.3603118384277852E-16,1.1369924690629073741439166466214458001620933296122
+1.507694484529106,1.86831139230828E-16,1.2278821134494574210905742863482895485229814670410
+1.5029685397618773,1.6227945280146267E-16,1.2259561736709340614333649356558004704318178042711
+1.6409962392755661,2.1885852610426223E-16,1.2810137545223963626735642778597996492912390332168
+1.181571178718238,1.9722540021830665E-16,1.0870010021698407388066108441606687665092731229294
+1.9743473865767427,1.273520799689311E-16,1.4051147236353132807796176896096798165663146571181
+1.1218585158782581,1.1119884133931232E-16,1.0591782266824872022067774475925526350261446115599
+1.3540530591416022,1.9137256549660956E-16,1.1636378556671325586804278026292887420041161500840
+1.0637986673716222,1.420038052113116E-16,1.0314061602354439838354444129069545854929500872333
+1.1220810339167595,1.4342810490331853E-16,1.0592832642484066218919723789977601392768046837230
+1.1906124019143538,1.8408318145178734E-16,1.0911518693171697929520358726699599352382114596943
+1.2731352730985308,2.0471598541763096E-16,1.1283329619835321513434047891900426296536933446068
+1.8986979173756207,1.4878585434200528E-16,1.3779324792512951021739649789793322758391667904217
+1.9946236070521948,1.4882226081619285E-16,1.4123114412381551325835894420089814603238434989106
+1.7581151398689692,1.891474445304287E-16,1.3259393424546121962705953901589214156943924482442
+1.8981714925564386,2.19583902225201E-16,1.3777414461924409992612419350075474380862928199654
+1.6149675337040568,1.2113358308061926E-16,1.2708137289563946511728679318280719684281242413174
+1.7408841230062602,1.4289903966689573E-16,1.3194256792279966488541419465318138325824466893566
+1.4531140052943052,1.4367930062984487E-16,1.2054517847240118366948923800969010390752048634390
+1.086792499206435,1.476313242419068E-16,1.0424934048743115220392590043028056554878188398181
+1.7388510724953952,1.9879669410315942E-16,1.3186550240663383472353268527044860733100239459613
+1.5940764625649972,1.214058744353613E-16,1.2625674091172310014114885269741789154461082030804
+1.911489546648798,1.3110983349828415E-16,1.3825662901462620755162280328941463346966996572055
+1.4145491385344418,1.8511312680441864E-16,1.1893481990293851440999029660253248259601269523802
+1.743311352983762,1.80866504100537E-16,1.3203451643353575116036127666444238938958264844399
+1.1029686242294536,1.9711190783145272E-16,1.0502231306867383162653511150812628805342268643591
+1.8863826632384129,1.5146352427347644E-16,1.3734564657237640350092184194678921687066626837229
+1.7189940460104243,2.17057035783949E-16,1.3111041324053648513761594770138470240360883736194
+1.4334189797903618,1.350132749415956E-16,1.1972547681217903375017582977954406583246855804780
+1.859009251009701,2.0309024457190608E-16,1.3634548951137698486277948978989262246712018059817
+1.4215665992555082,2.0134930127224796E-16,1.1922946780286777801690548438461789820577183445088
+1.931531892517121,1.8661149044745758E-16,1.3897956297661614001953108458872729900056357575485
+1.429909357597708,1.9072187037481624E-16,1.1957881742172014710282074807582457047605297213692
+1.3619659397549795,1.6292593576645852E-16,1.1670329642966301597046233382048742103866693288785
+1.7253203915992343,1.7418890660523339E-16,1.3135145189906484287461013367437743550533268961110
+1.8260880512174187,1.129672646276178E-16,1.3513282544287375319062493636851589628458940242930
+1.6622756329985908,2.1457069316165775E-16,1.2892926870957544676165980213918533940086558309780
+1.1095516210175869,1.5832226453894123E-16,1.0533525625437985436602204765560233937576738839977
+1.4256886598631906,2.1835230058108847E-16,1.1940220516653747369789615920500072600430409666166
+1.0512777711053842,1.676253366917682E-16,1.0253183754841148834770705436840601028723973862210
+1.2080653129595815,1.2828620055475347E-16,1.0991202449957791837842972725426985953229461100728
+1.7076846947744848,1.9453416547495956E-16,1.3067841041176178185133525075081461212058878658132
+1.366059015546873,1.602485946353946E-16,1.1687852734984613908000864034567963385420785390376
+1.9240190440256781,1.2471717593715453E-16,1.3870901355087484950491339137933786720882457794491
+1.7963163133827444,1.829319045803248E-16,1.3402672544618646423377956335593875763879071472861
+1.3708334549067307,2.1600194377819273E-16,1.1708259712300248224850715522166504588479084376926
+1.1558119040076806,2.02799089398499E-16,1.0750869285819081326586519389331166158331522526230
+1.1882562240075825,1.3677032530258362E-16,1.0900716600332212099662274699379839958470983802034
+1.4937148054178122,1.6772861322232181E-16,1.2221762579177409203930522524438282747784341990080
+1.129185266067075,1.6693375387455433E-16,1.0626312935666232716546148098633452652437887992284
+1.4491834815243325,1.5668062301009159E-16,1.2038203692928329388503310220586239079986169162001
+1.7710937450764095,1.7985386411466207E-16,1.3308244606545258667520150676702764972061256906120
+1.376120117701203,1.4694849668489325E-16,1.1730814625170765024015895299389665618725569731036
+1.0715485540218406,1.3062686716609097E-16,1.0351562944897937980969710880618655621377438811106
+1.85549114387266,1.8215624933555214E-16,1.3621641398424274299946804169583118302264519712600
+1.6634871327271494,1.3998429224355535E-16,1.2897624326701214517806601449430198914035836069477
+1.4688501543271981,1.1688382093797395E-16,1.2119612841700836434272213437665306309067423080463
+1.3616656500002853,1.7340384462016268E-16,1.1669043019889357878088822956634957327448657692320
+1.731326015175728,2.0298744904806208E-16,1.3157986225770751015450660316326940536570253948529
+1.9122278019215737,1.823506418792782E-16,1.3828332516690412204820754308709967267760607191372
+1.3315110920853819,2.2183047350963022E-16,1.1539112149924629696401180914900874569513056053293
+1.0379569118409204,1.142956699843983E-16,1.0188017038859527451052759272967083335524453544198
+1.6741621792523833,2.0669308393556774E-16,1.2938941916758045002413947046966299263535662354178
+1.0471514552269294,1.7733069750445678E-16,1.0233041850920622545871834515053934749102255469346
+1.1389298204976264,1.2462517552265735E-16,1.0672065500631199231422935513035125259300909839168
+1.3948527550933858,1.7415850161409952E-16,1.1810388457173565690149951774280901667448905662169
+1.0011642125571836,1.316822527032659E-16,1.0005819369532830550881835540078623632499200281763
+1.7335572636333416,1.267450168001598E-16,1.3166462180985983233432580279467692162934971900735
+1.8297128163780103,1.9643241215451181E-16,1.3526687755611166268365591721654902372313846570613
+1.6455352624811823,1.13242163117596E-16,1.2827841839067015983804894585009949062044066620356
+1.9782206577159036,1.3942579074372795E-16,1.4064923240870899779712845052500479221260758824164
+1.9463427571057939,1.9521687247361622E-16,1.3951138867869511848379655794558667476975723162920
+1.946605813364436,1.3417898062010335E-16,1.3952081613022610463040381112592432114597453870052
+1.612397708349986,2.1690947846054174E-16,1.2698022319833850007135723108173338605429080426969
+1.1883626728176673,2.1834365813214104E-16,1.0901204854591383626396826392652505335563203883083
+1.9133771299721867,1.326154209987531E-16,1.3832487592519961508508975891669252940891249512922
+1.8347139636922816,1.860704330725183E-16,1.3545161363720558329961467515304144763935106429121
+1.1243569645501101,1.4244301027850345E-16,1.0603569986330595634132968239881689807265369002562
+1.2324741456877062,1.379313669936293E-16,1.1101685213010258689996325025446170872570637132184
+1.0464033960998174,1.3882421776415817E-16,1.0229386081773517170026436069172935011346854416847
+1.2793276909528513,1.1845268117867398E-16,1.1310736894441720582559541674789927545268760718791
+1.228562953797671,1.9699931559411234E-16,1.1084055908365273104843159875512747332140362342858
+1.086188510828385,1.69396232887815E-16,1.0422036801069094517566447423056901370582536990704
+1.6410788517624522,1.7680547073029697E-16,1.2810459990813961161578520346970076991670082239995
+1.9468625539761444,1.3638262138486665E-16,1.3953001662639277846758972908102284408402731573450
+1.9405705420495276,2.0108854921347042E-16,1.3930436253217369411728110163335277721076118872042
+1.7027925029890594,1.6450631829706605E-16,1.3049109176449783769574256787360338339909521370140
+1.6402223137193788,1.2611119116862885E-16,1.2807116434699025926576879809858364849745263006248
+1.415694284491273,1.6283385684203572E-16,1.1898295190871981196703608587099381709969350015143
+1.3021609848337434,2.1691833849789086E-16,1.1411226861445458292625411200478562253359005978637
+1.8484307608402062,2.153393396433374E-16,1.3595700647043558688111204786797600304048517811738
+1.1985654116476105,1.7662085147536703E-16,1.0947901221912859049008404402466210657995462384296
+1.2779473820939402,1.5674950497270245E-16,1.1304633484080500547730092790101447005815210843337
+1.3016924821515956,1.3460968244029614E-16,1.1409173862079566642537000831754729051157795400738
+1.5678399323199717,1.6640040837723125E-16,1.2521341510876427726139960527455625184277023436257
+1.3242788028047048,1.3708067095755263E-16,1.1507731326394029594827701995368946944432908059868
+1.4729763749701326,1.8133607822356558E-16,1.2136623809652059673239486293828511867472822658137
+1.991722358928556,1.6121731957786648E-16,1.4112839398677206450687419633071099635811218950945
+1.0906192635194563,1.7096446062783983E-16,1.0443271822180328713077779312174287683337343747264
+1.4865538050136664,1.950336519373172E-16,1.2192431279337466916807967733920576981348360700424
+1.7704474182664716,1.1514517860386104E-16,1.3305816090215855967708939035605747071441725954036
+1.6254050507876523,1.1206920436097648E-16,1.2749137424891349906166431985679309488829007068779
+1.1853179670737082,1.8081742230448742E-16,1.0887230901720181480435679908784787808645835318281
+1.7775604155371652,1.7985414523595675E-16,1.3332518200014449372070447280544625859143275802492
+1.3961945553872575,1.920281456897939E-16,1.1816067685094130520027124811908896604249404986217
+1.3761414206256366,1.5617630216248552E-16,1.1730905423818047124841674011924399568059733226265
+1.4713066973194828,1.5765932039973335E-16,1.2129743184913203195970519194169025453179548499240
+1.8607653840309188,1.5520138841842654E-16,1.3640987442377179318299481962111160996242707488543
+1.628604019371848,1.4751131287103399E-16,1.2761677081684241604720597669606050688853949398846
+1.430873963248291,1.3497378408658238E-16,1.1961914408857351060875863805734241196305437627795
+1.0784439632730103,2.102757387710058E-16,1.0384815661690921400200539067983549048665417004953
+1.8395972594280032,1.5587451334578157E-16,1.3563175363564401634220851875521599958346047495432
+1.2685870113918296,1.6491659163566155E-16,1.1263156801677892719283782141315931852339752449249
+1.330342533123091,1.2282603760059787E-16,1.1534047568495159639362736580110681280423975076017
+1.4164654895266333,2.1918386296944453E-16,1.1901535571205228027241855929755574827614806327605
+1.2604529316361683,1.5380209422730746E-16,1.1226989496905074118825468658754461989746470427219
+1.2952096441309666,1.763348276436848E-16,1.1380727762893578661457708771558018796252910198640
+1.3305790939804067,2.0443021425611236E-16,1.1535073012254438915297609833337489034619166604035
+1.7930528425656398,1.575531796433184E-16,1.3390492308222427526861446237899129900802731010861
+1.0039233118377844,2.0438578794326005E-16,1.0019597356370088121782057065980622627064255430765
+1.6139263082597592,1.365178481055671E-16,1.2704039941135888062648104076968877280094149359331
+1.8236691518262105,1.81723893126016E-16,1.3504329497706321364817255442851652793624925592780
+1.432224901347879,1.3809886225210783E-16,1.1967559907298893820975156883683223915767523334153
+1.7940007191260836,1.91881101334219E-16,1.3394031204704891986240858839646152294637243941215
+1.4998469656183941,1.1886073900864399E-16,1.2246823937733383878651479292340002513976768749654
+1.3177020628290117,1.8344161924674624E-16,1.1479120449010942769356540933027360872835655268310
+1.0703607162130069,2.1817930103731948E-16,1.0345823873491212192771421109115003743333507854430
+1.1982238614026202,1.721761323685187E-16,1.0946341221625700504071648724712117299851835410650
+1.5489205928211558,1.282732555959173E-16,1.2445563839461657444938920850373616393660404551957
+1.6841289603046072,1.6600948815434176E-16,1.2977399432492657165042869729785261476738780996858
+1.8203932641961253,2.1158879956698594E-16,1.3492195018588063533264686098225430197347377447237
+1.573799867430269,1.9176723347905876E-16,1.2545118044204562552899263404842676204501578738695
+1.8762050302487567,2.0081417372633678E-16,1.3697463379212798370210504721512795487089269611283
+1.087664648074917,1.9636716463072976E-16,1.0429116204525277150861186598073354379548597089887
+1.467767859367766,1.4227288240963965E-16,1.2115146963069685543444947036766878622955705012180
+1.452125683226246,1.8687854569553975E-16,1.2050417765481187242699334946818178537463462189406
+1.0624284950670249,1.5008411971456546E-16,1.0307417208336067971468121033274190359579847691999
+1.3975761313064081,2.1007199925680286E-16,1.1821912414268718183094304277866794093421911752075
diff --git a/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt512.csv b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt512.csv
new file mode 100644
index 00000000..c430d7da
--- /dev/null
+++ b/commons-numbers-core/src/test/resources/org/apache/commons/numbers/core/sqrt512.csv
@@ -0,0 +1,537 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# High-precision sqrt test data for (x, xx).sqrt().
+# Computed from uniform values in the range [2^e, 2^(e+1)) using JDK JShell (Version 17.0.6):
+#
+# static void sqrtData(long exp) {
+#     MathContext mc = new MathContext(50);
+#     // Target unbiased exponent
+#     exp += 1023L;
+#     for (int i = 0; i < 500; i++) {
+#         // Combine an unbiased exponent with random 52 bit mantissa
+#         double x = Double.longBitsToDouble(
+#             (exp << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         // Combine an unbiased exponent shifted 2^53 with random 52 bit mantissa
+#         double xx = Double.longBitsToDouble(
+#             ((exp - 53) << 52) | (ThreadLocalRandom.current().nextLong() >>> 12));
+#         System.out.printf("%s,%s,%s%n", x, xx, new BigDecimal(x).add(new BigDecimal(xx)).sqrt(mc));
+#     }
+# }
+# sqrtData(512)
+#
+# Note: The value (x, xx) may not be normalized. However these can be added exactly
+# using two-sum to create the double-double number before calling sqrt.
+
+2.2389049070416752E154,2.9020033170860478E138,1.4962970651049461774300494142657566968937822317297E+77
+1.9117092323620837E154,2.57314015184119E138,1.3826457363916774281120966692674420272057452913488E+77
+1.5008375799749535E154,2.766953305502905E138,1.2250867642640474603976530825844655774359665417490E+77
+2.080829822969566E154,1.5911287898593448E138,1.4425081708501917870840909966110817691893581432927E+77
+2.6014217453586464E154,2.400262269848878E138,1.6128923539277649724565429776801399834103349316807E+77
+1.9007597037934282E154,2.6814380552994317E138,1.3786804211975407847913353048990023660770435160399E+77
+1.5231379581085927E154,2.6639912929143898E138,1.2341547545217305261228054074625082213065603183363E+77
+1.9078010245461506E154,2.2178508482045074E138,1.3812317055969106306275726941593403737245834422971E+77
+2.169744098737103E154,2.783128561780221E138,1.4730051251564276856641541752594303124006806955588E+77
+1.8381215108295965E154,1.749197946623481E138,1.3557733995139440921527159743032415065321312792454E+77
+1.384149398376316E154,1.9711324105772485E138,1.1764987880896079722824086119143116971697788311556E+77
+1.5972879167457358E154,2.1854597814612725E138,1.2638385643529540825645106360241830828805656147411E+77
+1.9035365059468873E154,2.6143964630866537E138,1.3796871043634812488941380442436053208284763342575E+77
+1.923810540273478E154,1.9391248381045948E138,1.3870149747834297017421714879295501205382825514949E+77
+2.6471257884431528E154,2.445552536088575E138,1.6269990130430789592773945987756809274388378695285E+77
+2.544783833251938E154,2.5326696700170557E138,1.5952378610263543236173992933795723278681419608861E+77
+2.30066532480907E154,1.7320385297090053E138,1.5167944240433738832787263310900394882622950226082E+77
+1.353754287431343E154,2.4104556444319886E138,1.1635094702800416766849551310620424893411075671955E+77
+1.7652973467566075E154,2.619446751136237E138,1.3286449287739022567565840992562256224274389728683E+77
+1.3786059724890175E154,2.1296902094163262E138,1.1741405250177755977655332117737276101312595370735E+77
+1.8489431401794563E154,2.825514280653714E138,1.3597584859744235398191951920288829303500389984911E+77
+2.179270589167954E154,2.9630453920858467E138,1.4762352756820148692048000350968196003433397015875E+77
+1.5462740808562076E154,2.798762892309621E138,1.2434926943316586743632053407816810396399433469512E+77
+1.6187641330869574E154,2.006712411074468E138,1.2723066191319439844539030061852073142881498180497E+77
+2.6442086899911352E154,1.9380316814591936E138,1.6261022999771986556240880407233333095909563198869E+77
+2.3315101632468943E154,2.698689804038844E138,1.5269283425383440624130168205978280715163596428161E+77
+1.4646712486570026E154,2.2980691728904374E138,1.2102360301432951474618653541037353240431436421421E+77
+2.636158146213505E154,2.517803881350797E138,1.6236250017209963320322244729682486351702577881770E+77
+1.6937231837132132E154,1.7195696794507388E138,1.3014312059087923193691644370812434955619671191847E+77
+2.013191521109023E154,2.2896648334923348E138,1.4188698041430803618237782200541017837899245137408E+77
+1.7634329015238558E154,2.698724157601647E138,1.3279431092949185937718224548790602396481054808906E+77
+2.6105851837516545E154,1.832273283521835E138,1.6157305418143381438200487642209144781595805823121E+77
+2.293981894358596E154,2.5538432783811298E138,1.5145896785461719010093490390562814361523331851994E+77
+2.0827577737792427E154,2.8196391422922456E138,1.4431762795234832164522896812313422610720601606142E+77
+2.4651089020079597E154,2.8397092480414796E138,1.5700665278923565205514448303458899870195850711455E+77
+2.313933753527434E154,2.016788502036494E138,1.5211619747835646759369908533904377046213556734968E+77
+2.6021359338712056E154,1.7293657987580258E138,1.6131137386654438115352435376116226231229958015086E+77
+1.795418788199539E154,2.2790986208425143E138,1.3399323819504994592151572746154871070929280546739E+77
+2.464666112451961E154,2.842212888145687E138,1.5699255117526949795065175321605662680612572448760E+77
+2.3075047253900564E154,2.256123775380716E138,1.5190473084766177248014355159975950611212763574780E+77
+1.7771309646642948E154,1.843743441410356E138,1.3330907563494298495349563554552729678877663132017E+77
+1.7119672863929493E154,1.769205842910677E138,1.3084216775921092635426042091240834625551305042126E+77
+1.720828165987207E154,2.6171594614063836E138,1.3118034021861687569936216572818016629127038571297E+77
+2.193516057161811E154,2.7824593435648653E138,1.4810523478803209351716726220073699565837819681166E+77
+1.9831014951907385E154,2.5113838088460417E138,1.4082263650389232547215774817337554696149338021666E+77
+2.311817154261108E154,2.2271097957534266E138,1.5204660977019869733016747089923293562004835972896E+77
+1.692811483393013E154,1.8779525803649992E138,1.3010808904111278338996507352912730725365328153027E+77
+2.5303332869922042E154,2.2012306285222013E138,1.5907021364769094993437050106264437080576013843356E+77
+2.2160213747506483E154,1.9597239538272792E138,1.4886307046244372297276303476900414157351293979255E+77
+1.9035196564612464E154,1.613937767450968E138,1.3796809980793555051662822186670716331418859656512E+77
+1.994673332068313E154,1.7187026521620353E138,1.4123290452540842759705410335397283509043266322234E+77
+1.350536762203317E154,1.5128041730754206E138,1.1621259665816426149563951036140403642170504641058E+77
+1.738415183044009E154,1.9808988588588926E138,1.3184897356612258831166554666414624837027124323193E+77
+1.3933331807780415E154,2.5904532839320986E138,1.1803953493546311510322291377058100835588694633986E+77
+2.3266571203873654E154,1.6247880108418326E138,1.5253383625895487222514137222575238211977334511483E+77
+2.5114601783158257E154,1.9610428734503913E138,1.5847587129641615974838925716754496838439002570802E+77
+1.804870141152514E154,2.4673717861058565E138,1.3434545549264084160942657583538211956843131773041E+77
+1.9820561077631815E154,2.3338072730651817E138,1.4078551444531435680540629612291344291087929462853E+77
+1.3769058901849775E154,2.7981814169674015E138,1.1734163328439645422793083303044804059293676575887E+77
+2.647769269662509E154,2.350807484151144E138,1.6271967519825343278707613182760048975011050503672E+77
+1.6763097120958482E154,1.699254202073271E138,1.2947237976093002921763834685805405826579870107044E+77
+1.6907556728765634E154,1.86271581205903E138,1.3002906109314808182496767783072514959830867160975E+77
+2.4548292224330117E154,2.9298717813951902E138,1.5667894633399255888149915660306117501934412562132E+77
+1.941740829962871E154,2.2801189982740047E138,1.3934636091275836409207447676025642220226333114037E+77
+2.1070264098908806E154,2.0282545203938777E138,1.4515599918332279412818366599879223917066677620048E+77
+1.6428972803290234E154,1.916561288420857E138,1.2817555462446899811299386170852467092528184950357E+77
+2.5363663378627472E154,2.6709657700696307E138,1.5925973558507333885642707892882333200014920463879E+77
+1.8505858633084285E154,2.0146069403403757E138,1.3603624014608859619690681889511672487761508074922E+77
+2.1648027531812213E154,2.1789655569657735E138,1.4713268682319444078936071841539123143737282105428E+77
+2.0084054924028562E154,1.6072833425920232E138,1.4171822368357770180973811378661629081861338233243E+77
+2.5263248528654614E154,2.6233727420884588E138,1.5894416796049679713365533770651272667275929784054E+77
+1.4861800871135345E154,2.774698791328993E138,1.2190898601471241725425159730613090119630684832303E+77
+1.933672376416702E154,1.779357576832074E138,1.3905654880000086152449663787517641837449204229049E+77
+2.3594924208579377E154,1.633622716116463E138,1.5360639377506191242280245335765575120037218796923E+77
+2.215679325466386E154,2.4162070535992906E138,1.4885158129715606910941732034850088560807960264625E+77
+2.5831890710565012E154,2.255953227404123E138,1.6072302483018733327684788846557378221728632553374E+77
+1.7546921443916768E154,1.8973781530093424E138,1.3246479322414982802783186994527804208991415209670E+77
+1.9902746138845465E154,2.0283835471235054E138,1.4107709289195559342366111872156158746915055516243E+77
+2.0436959593859517E154,2.7170887084319736E138,1.4295789447896719515850554070898346826064745569619E+77
+1.812697201810696E154,1.9154250799013175E138,1.3463644387054703374019484484190933179647401658612E+77
+2.6605936381143266E154,2.3701380497390755E138,1.6311326243179390371749761894185580167338776658793E+77
+2.2870692648957044E154,2.0660152719180192E138,1.5123059428884436717701455124810974044484078369981E+77
+1.4829685183308483E154,2.6875009802120336E138,1.2177719484085879355606208002364893190521724001801E+77
+1.8876206810916675E154,2.199368192041975E138,1.3739070860475491723166598948427869103879820428971E+77
+2.386725998103663E154,1.6290036631281257E138,1.5449032326018555835489678606097279935865320144214E+77
+1.4370175461108845E154,2.6538125173282526E138,1.1987566667638953028425703068970027233111357409671E+77
+2.2480340204710297E154,1.725584834479617E138,1.4993445302768239517026418444488463697915790788108E+77
+2.5979005008723573E154,1.69038467860009E138,1.6118003911379217312489451582449246839044017100235E+77
+1.626253682031921E154,2.2758006774076708E138,1.2752465181414616616008699342760579034034236109087E+77
+1.6432114318056177E154,2.374572251604259E138,1.2818780877312857698338191177092833842856270061472E+77
+1.7349272056369858E154,1.5674704312608502E138,1.3171663545797796866820913336950484979037043068682E+77
+2.034911024412974E154,2.3169464449360387E138,1.4265030755007064125051935885930322194749386018557E+77
+2.5310837550776798E154,2.8996978103896787E138,1.5909380110732410502012292012905930551611817083252E+77
+2.297553097934143E154,1.93973664285687E138,1.5157681544135115749057984491617187781364294483536E+77
+1.7617295558794316E154,2.1920572451298033E138,1.3273016069753821514608105964436315011000013590101E+77
+1.6896176661964443E154,2.1175081631405055E138,1.2998529402191789964700576676526194041802066593294E+77
+2.2298663316371392E154,2.3850359812913276E138,1.4932736961579211709205284776761378578670379800599E+77
+1.6915822360766828E154,2.4559020234648546E138,1.3006084099669212259463299499784830580702671819968E+77
+2.3454672470733122E154,2.322618162547342E138,1.5314918370900030566529267259787442220994859459972E+77
+2.3689522446849245E154,1.7275592746663142E138,1.5391400991088902953781159747428199171003971188100E+77
+1.520390730546197E154,2.4994179890299746E138,1.2330412525727584898712090683557614313175284327785E+77
+1.9458918159592622E154,2.4760487344528504E138,1.3949522629678989917683152557629269164813993884677E+77
+1.7241742808693275E154,1.977070114320597E138,1.3130781701290017492389199993412315565204337142465E+77
+2.4194448658057046E154,2.961387505615235E138,1.5554564814888601908407725914941137728749483791775E+77
+2.6139067208568678E154,2.6317335745384214E138,1.6167580897762250207597121146806316608168251619582E+77
+2.1572549549383613E154,2.5128079889592257E138,1.4687596654791285298230273522510814735944900074704E+77
+1.9391111875666495E154,2.552693535138885E138,1.3925197260960613452148079674726145042246296474416E+77
+1.720783145894339E154,2.489933248473609E138,1.3117862424550500166418059391186628037662982142430E+77
+1.8820406711798423E154,1.492935106897527E138,1.3718748744618958222690534220744121134815269729582E+77
+2.5274572910661765E154,1.8216590775783028E138,1.5897978774253589506617748335259330302129003376479E+77
+2.337255716026618E154,2.904351907643665E138,1.5288085936527889527214473170231268705470297145370E+77
+1.6282568156126488E154,1.534403466426253E138,1.2760316671668650025117731683670084074774554973115E+77
+1.90656224804539E154,2.5539765364124416E138,1.3807832009571198848817006358231883382182858726594E+77
+2.3397880128538543E154,1.954565624950642E138,1.5296365623421318028207558490387747491551293590276E+77
+1.6699106559414193E154,1.9931307054699622E138,1.2922502296155414189821728434137895519406117901748E+77
+2.657890039239053E154,2.6515037678298298E138,1.6303036647321422377597746691689442479758215724977E+77
+1.4173194475150793E154,2.1082071530481933E138,1.1905122626479239924923426810547770718443901544095E+77
+2.484831631908247E154,2.8593236920263592E138,1.5763348730229396832411405904270128534391838079576E+77
+1.4099384751231435E154,2.545659808038357E138,1.1874083017745596748589510242831089197748407269121E+77
+1.4152556088259936E154,2.8570031209972453E138,1.1896451608887390614077705862089638990601001094270E+77
+1.4828583541018652E154,2.8206566288374228E138,1.2177267156886496950598031192131879104832185717677E+77
+1.8529257489321463E154,2.7025993278812594E138,1.3612221526746273932378782563981541475121867345094E+77
+1.808115377692636E154,2.12815128106293E138,1.3446618079251883782417684666062017065054890366199E+77
+1.7322676696533252E154,1.9773705125732032E138,1.3161564001490572932014060156339863057310441918641E+77
+1.583240426769522E154,1.7637401518570563E138,1.2582688213452330655444829141872143463502394960435E+77
+2.3727078889497794E154,2.2166168955830734E138,1.5403596622054797159378071573808917601637213203792E+77
+2.3686702637183606E154,2.5771433366245793E138,1.5390484929716675013616951362554992457799354907276E+77
+2.029236649647678E154,2.3612600369239394E138,1.4245127762318168632582971330607128026774343477385E+77
+2.2272381125743253E154,2.452746372560932E138,1.4923934174922930032371881376321516021681386267697E+77
+1.4212978600658498E154,2.8216238713556556E138,1.1921819743922695734012562701478202747128031058494E+77
+2.3453856817488064E154,2.6623103632320953E138,1.5314652074888304876812441973738310110292135246804E+77
+1.8474329007968428E154,2.571672759303952E138,1.3592030388418218469431901434848667202868779839428E+77
+2.6103260269256587E154,2.5665596327911608E138,1.6156503417898499582040952635117968011463806137245E+77
+1.9220089604966596E154,2.4713746066363703E138,1.3863653777041100938128028330996116875201647595520E+77
+1.54605589914945E154,1.85642413855073E138,1.2434049618484920954173567386120051095006407584546E+77
+2.0579134659009055E154,2.2877812906365437E138,1.4345429466910029559095387954997808597270877600677E+77
+2.1069894336419074E154,1.923960996657169E138,1.4515472550495583619250983803125175421179995541430E+77
+1.5276207697769856E154,2.240414828232683E138,1.2359695666872165539803110368410402472942797191712E+77
+2.2331954668705476E154,1.6107300121841345E138,1.4943879907408744258686156520260071186260001867850E+77
+2.4626809769115076E154,2.832550686274315E138,1.5692931456268799517506648950927929491798186042896E+77
+2.672392764588786E154,2.749619972024045E138,1.6347454739465671387572075379993007973026628736524E+77
+1.721291389502588E154,2.510982185049611E138,1.3119799501145542741455234849060600868023237604001E+77
+2.0295557301375358E154,2.7173543711233634E138,1.4246247681889909151908707375798794525927024924412E+77
+2.2334828969898657E154,2.532352872457406E138,1.4944841574904251717436641508951443540957806472240E+77
+1.6363086745521027E154,2.023724142911033E138,1.2791828151410192070264170550792922498195437385551E+77
+2.50046934215629E154,1.6207944012856908E138,1.5812872421404942484459819579962001213802147053321E+77
+2.5158133847970835E154,2.7436946768592933E138,1.5861315786519994161737128192666895403882702838363E+77
+1.9629865926584734E154,2.8971252323564144E138,1.4010662342153827443773821603912864154576517555998E+77
+1.4653748714896028E154,2.9601307196746205E138,1.2105266917708188777007433108836611110192109302289E+77
+1.9097310441263148E154,2.510183214677742E138,1.3819301878627281326234717386995153091904190361369E+77
+2.2534065375623877E154,1.8715284495047843E138,1.5011350830496194585730334173983756652608853122171E+77
+1.6622462918022874E154,2.4086913324676532E138,1.2892813082497890965883095919495308208719148205130E+77
+2.1401006643436464E154,2.346205647329052E138,1.4629082897925100739705873862477660542252043219884E+77
+1.6305282042228052E154,1.679070307120889E138,1.2769213774633132843881718058776771783286140410592E+77
+1.8302747434385854E154,2.0817554742385136E138,1.3528764701326524265877850632310940285075746840446E+77
+1.9442059166275063E154,2.624118558412275E138,1.3943478463523750778018947193385755981683966589958E+77
+2.2846846847261756E154,2.4098059306197944E138,1.5115173451621968925541249238851615868346766482634E+77
+2.453321257434554E154,2.0803341160003053E138,1.5663081617084660741269714909809375192620777630244E+77
+2.3071253886309308E154,2.5512759325995086E138,1.5189224432573675707164993247647483563421676543797E+77
+1.6886809549314682E154,2.001261483091511E138,1.2994925759431903724189882976779231453416969315331E+77
+1.487712249307686E154,1.8504905333269174E138,1.2197181023940270240570235808396930607621644913569E+77
+1.5011814992964033E154,2.1781406790216935E138,1.2252271215151921744574917372720267878444658408889E+77
+2.20734295354121E154,1.580692762999508E138,1.4857129445290601957084503093289653638628415354839E+77
+1.6297552437175654E154,2.5277374696013498E138,1.2766186759238506473858634211327414738697898241768E+77
+1.6494753752438348E154,2.8282900121615526E138,1.2843190317221944359325933143586592794052948609959E+77
+1.5058744495338103E154,2.6464890946923303E138,1.2271407619070481230796025184113994321696145865297E+77
+2.6023654749779978E154,2.645446009640347E138,1.6131848855534191591308798846368318292320430357734E+77
+1.8914555666145117E154,1.9973766292293162E138,1.3753019910603313996738352676333796760423248375355E+77
+2.0491095907458817E154,1.690672802588221E138,1.4314711281565834892076885998889122705527331096855E+77
+1.416225536275813E154,1.6356332311975727E138,1.1900527451654456983755058241674019960683857231292E+77
+1.4425367299771692E154,1.531984281250888E138,1.2010565057386639694185987746069074326776632823610E+77
+1.508637620579779E154,2.8400657939817386E138,1.2282661033260582681339754962511577108282973316711E+77
+2.062116054646212E154,2.1678348347404328E138,1.4360069827985559293596510879711411126326870790667E+77
+1.9981453171171375E154,2.7783577791138356E138,1.4135576808595883392438327914643645170766459625669E+77
+2.3167874035716577E154,2.2171046495027968E138,1.5220996693947666550665104995094223090775439928199E+77
+1.9150456139335243E154,2.4584407132124346E138,1.3838517311957681489505166536605010884143239732018E+77
+2.2922159249679747E154,2.092627103857651E138,1.5140065802261147809142939035309370213309808531824E+77
+2.6475825101402892E154,2.5990748978957256E138,1.6271393640804986117605014699921042037086805104924E+77
+1.999699148780117E154,2.7715391067464362E138,1.4141071914038615104405645776955353913296694898655E+77
+2.6732668792314524E154,2.4799822275443482E138,1.6350128070542605451307689194276597983421133689260E+77
+1.7436244032213832E154,2.7122776016717924E138,1.3204637076502267498587101289670407292506132261106E+77
+1.473482695585017E154,1.710361972016213E138,1.2138709550792527457760202086623223995672766850784E+77
+2.4347744955355698E154,1.7898401855610635E138,1.5603763954685965496949249023221204908815450680025E+77
+2.1444964857131177E154,2.4210962232135086E138,1.4644099445555257253192147413143067210721878673826E+77
+1.541151037464995E154,2.0134428965508166E138,1.2414310441844908704168197491462079525273240845878E+77
+1.902353873920676E154,2.19947519573707E138,1.3792584507338268759072040198123689904495920698382E+77
+2.314785525491157E154,1.8358261562630343E138,1.5214419231410567387692795256949200962043485627896E+77
+2.215498663914426E154,2.553932374422471E138,1.4884551266042340020844808558631828613268888018075E+77
+1.838680571789427E154,2.9471778075787072E138,1.3559795617152299664911359827840673016301813829568E+77
+2.502691765565946E154,1.7618720855015046E138,1.5819898120929686092010906930914767625325103582304E+77
+2.583644524321064E154,2.9372484591608065E138,1.6073719309236006112920226627951441923369285665150E+77
+2.3175707034780134E154,2.777454057463095E138,1.5223569566557028853254402075398931091951676097679E+77
+2.361823850474984E154,2.329617624163882E138,1.5368226476971844551490039578599677585467908777377E+77
+2.556191592153482E154,1.6055034001767866E138,1.5988094295923707823701371909692717512873013815169E+77
+2.218167742086646E154,2.2071723106963464E138,1.4893514501576336467074945350140630013894164511543E+77
+1.7265215147034642E154,2.8051755272068353E138,1.3139716567352069182618199614867237410398874147138E+77
+2.1287995650444885E154,2.6574085512226484E138,1.4590406317318543989282604135127700718829340624529E+77
+2.2533057209480922E154,2.3132908908116138E138,1.5011015025467439674289455946229351301353256775168E+77
+2.6461819580991537E154,1.9701994474118367E138,1.6267089346589186071599921598918802615778453099026E+77
+2.6298425190064834E154,1.6433424794591663E138,1.6216789198255256090716069768671347047381831389671E+77
+2.1420728964381686E154,2.718287028054097E138,1.4635822137612116112162892505041890148681795264227E+77
+1.891564615719687E154,2.094946516309466E138,1.3753416360016470729455066410555759086213791796374E+77
+2.0100499813226757E154,2.098729379513133E138,1.4177623148196159817296262691846773512392764192245E+77
+1.9237156003274105E154,2.7593920414233125E138,1.3869807498041963804023218934054606505278694185855E+77
+2.1739269205476773E154,1.9558939832346532E138,1.4744242674846604229801152550217053012118165888411E+77
+1.7466132209354526E154,2.5626785585391473E138,1.3215949534314410614576160601028435632108726773987E+77
+2.5797063386948514E154,1.57113062601563E138,1.6061464250481185573225535885695043031743680126262E+77
+1.3521236371772022E154,2.0656571622269304E138,1.1628085126869352484647535461117215428342349671505E+77
+2.6071448732506036E154,1.5482251005497868E138,1.6146655608052720750682124426166444667283047790059E+77
+1.526915101878507E154,1.7653458913031797E138,1.2356840623227715928934154019048743871510110846210E+77
+2.5802608807594122E154,2.0968007895809996E138,1.6063190470013771166693902526944587234474555151981E+77
+2.09896900914314E154,2.2822777563609062E138,1.4487819053063646539694637968707481874398452264357E+77
+2.350216696980987E154,2.086350521573062E138,1.5330416488083379365710474849621525834993580423790E+77
+2.6319487811767326E154,2.6175946491240993E138,1.6223281977382791233000018437188134031564599861260E+77
+2.483745804941278E154,1.6149280996520957E138,1.5759904203202753330755615263324662359027676724461E+77
+2.0116279449597632E154,1.682050542900861E138,1.4183187035923073482825527613591004492551418509613E+77
+1.573587293117194E154,2.20853627310768E138,1.2544270776403043290362387742013648441364196950103E+77
+2.0575049255984468E154,2.7903361917391296E138,1.4344005457327625505625033187210195706273043985116E+77
+1.7242130385199154E154,1.794115092871339E138,1.3130929283641411645973755288358412787560264792758E+77
+2.321056523747179E154,2.0857131923423017E138,1.5235014026075522670747917772599252876400848102200E+77
+2.6247003775197118E154,2.5740935163459478E138,1.6200927064584026682598239481777458887128766083585E+77
+1.5698073586310897E154,1.5906151460747474E138,1.2529195339809695400875459756896376884279947736789E+77
+1.6636098276532154E154,1.8150412385628703E138,1.2898099967255702642727912727355590510492192811449E+77
+1.559683372814619E154,1.657411430351701E138,1.2488728409308208197389204830289399332185909907673E+77
+1.754884272162646E154,2.6326968328948732E138,1.3247204505716088160081445750497875589350472576364E+77
+1.935022166896765E154,2.1646023619976724E138,1.3910507420280416680957314778538021746587586082330E+77
+2.2473833936422927E154,2.122575587433267E138,1.4991275441543634582955720853352097107096343419121E+77
+1.9795495300997815E154,2.6513764041974808E138,1.4069646513327126955624614001783825504432554305030E+77
+1.6064301849424163E154,2.6216079380326823E138,1.2674502692186453372775422879700761485086426382507E+77
+2.2814239385468542E154,2.4616630311180984E138,1.5104383266280204029270659142487523684318228190472E+77
+1.7269708663297024E154,1.599812499826868E138,1.3141426354584583360508018472273523198599685472462E+77
+1.9523221013874667E154,1.6315070537623175E138,1.3972552026696722461674029821322622755230568379732E+77
+1.79226390888337E154,1.9919430974612487E138,1.3387546111529813650712955208929053484527359885368E+77
+2.478763027344216E154,2.7411290962046563E138,1.5744087866066475084170757268628475365862933652851E+77
+2.457579055798394E154,2.6601068290334034E138,1.5676667553400480978261435536336148825419856992033E+77
+2.5670423485322242E154,1.92876855841033E138,1.6021992224852140234982572883319295925089011979369E+77
+2.2256168275874915E154,2.2850841980251485E138,1.4918501357668241832061180491518702127940187291658E+77
+1.7736775583256364E154,1.8094759150230104E138,1.3317948634551931390832022892100853768707192045350E+77
+1.40281132938076E154,2.3670521467688273E138,1.1844033643065863693566400621967466012150655637734E+77
+1.629260619113028E154,2.5628899105059807E138,1.2764249367326808459286047903427999365461903526340E+77
+1.9524114385349E154,2.2836548156994677E138,1.3972871711051025657678609812610064595894700177417E+77
+1.6069456958613014E154,2.0143420247618645E138,1.2676536182495995851077189377937015104865498380573E+77
+2.111231822492586E154,2.6138878190880363E138,1.4530078535550267933390966945991074696770887097234E+77
+2.086162451683747E154,2.4527393205639988E138,1.4443553758281745570320764528091164320329229476934E+77
+2.246357410393108E154,2.5903987858963155E138,1.4987853116417668906981189139778013120006466707818E+77
+1.8133496159019617E154,1.6541420172662944E138,1.3466067042392005988077576161401246613098692038341E+77
+1.5524180914844325E154,2.700237242375027E138,1.2459607102490964005889650173986616733041675608485E+77
+1.6517190306440557E154,2.3522256634500113E138,1.2851922154464117167481930024635996486585613207332E+77
+2.1651261418826632E154,2.3789022749013454E138,1.4714367610885163246681327132165849172166928853520E+77
+1.9706026193827438E154,2.0761560651376928E138,1.4037815426136447002955152137936628537632579966055E+77
+1.495032641447102E154,2.6548101327243028E138,1.2227152740712378017135667362711652150303591583328E+77
+1.8387787732243912E154,2.229578458774414E138,1.3560157717461812250569117598521207040535196758395E+77
+1.5972460081824483E154,1.823469042372351E138,1.2638219843721854073823791280292863600153891834350E+77
+2.179054794443938E154,1.8665020669626266E138,1.4761621843293297330118953223458706801122475691628E+77
+1.749453052304786E154,1.653134259865862E138,1.3226689125797075755586574374034662518525289321206E+77
+2.592935394974681E154,2.837809516484615E138,1.6102594185331384998190190718198269048298236574447E+77
+2.3263281140563647E154,2.884544515078931E138,1.5252305117772739540398271489438634195617424278309E+77
+1.7082775703607308E154,2.659951275982183E138,1.3070109297020936139249145611999109744836635304002E+77
+1.4207369982494505E154,2.1307466829330414E138,1.1919467262631542069197835437700296630214879909575E+77
+2.5397480763751735E154,2.6302860338080255E138,1.5936587076206667513550310087031420743265453543175E+77
+1.658907158431446E154,2.0450160167169308E138,1.2879856980694491252826657319297188041326160239153E+77
+2.4050966827302322E154,2.2715998494361664E138,1.5508374133771188046744713208068167319377098004583E+77
+2.3252747195209706E154,2.8824017350615097E138,1.5248851496165115731814339715576469766104162317163E+77
+2.1363240059887244E154,2.936315641545814E138,1.4616169149228961740832263098161589807676376139781E+77
+2.38487438246409E154,2.6125303530524695E138,1.5443038504336153942220737725875445098839958454238E+77
+1.3567725675517257E154,1.760125848845656E138,1.1648058067985950214007640624238398514059993357135E+77
+1.8850096550486334E154,2.9576207439097606E138,1.3729565379314210810742674747686324576330410070741E+77
+2.1375105493070746E154,1.7325406311581512E138,1.4620227595037892452005664368528166012378855215323E+77
+1.785645531570636E154,1.711990265008158E138,1.3362804838695491031784302786554074270053464435785E+77
+2.1054761738668805E154,1.69362685734193E138,1.4510259039269011770878892291837659946555650108976E+77
+2.162055083017269E154,2.03411601906011E138,1.4703928328910166457973702704884334713895975363364E+77
+2.3732687232749126E154,1.990053333598937E138,1.5405416979994124058017902066680046200145811648964E+77
+2.3063965614579877E154,1.7407508598072877E138,1.5186825084453919014623879278805138250988893447265E+77
+2.0694864709224337E154,1.6028918840903554E138,1.4385709822328663407163488211095038700066992984573E+77
+1.7995584146761215E154,2.2652978314891605E138,1.3414762072717211301457421178506919048626838189212E+77
+2.352087700696272E154,1.8626765638409024E138,1.5336517533965369786192370003335387390015806631484E+77
+2.3688715690345975E154,1.84072306283064E138,1.5391138908588271462917919594826759331214532711237E+77
+1.4773662637625226E154,1.7953447451872344E138,1.2154695651321437907780200616644440187024809924252E+77
+1.480371929390828E154,2.084946448062738E138,1.2167053584951568281611659681951377822566808441101E+77
+1.5831142475347335E154,2.7530646219173672E138,1.2582186803313380143513965965201177177596783024652E+77
+1.9069058500829454E154,2.636899275736145E138,1.3809076182290203094364361404298618831710210776747E+77
+2.3724391507252104E154,2.706144383603775E138,1.5402724274378252457906150608431027458664951989335E+77
+1.550934984425705E154,2.2599837675806607E138,1.2453654019707248750552401831300595531610802095095E+77
+2.484383046718267E154,2.0090295624465888E138,1.5761925791978171704513526306309658378089044212326E+77
+2.543442212460101E154,2.0205313420602597E138,1.5948172975172112960136695857753380628838231192547E+77
+2.4089163764258186E154,2.3991611471487333E138,1.5520684187321829558136476215660083426985627205287E+77
+2.680890997342926E154,1.5903581580308226E138,1.6373426633856842523282499780318186583571408131399E+77
+1.553595172026682E154,2.342291530636459E138,1.2464329793561634640701580550565071085136189176267E+77
+2.022381736318493E154,1.9089803395725708E138,1.4221046854287813508358021416626457305035714611892E+77
+2.25037894360838E154,2.553547409541832E138,1.5001263092181205104184360561946033119525028615669E+77
+2.3859013655699316E154,2.8005572735266586E138,1.5446363214588513345230174541680784254216855239517E+77
+2.6672331984366076E154,1.69972509660475E138,1.6331666168632665832045065471968299961107952177783E+77
+2.4801100429952355E154,1.7153845664629962E138,1.5748365131007204656918296853844034485573012721605E+77
+1.7634819758924192E154,2.1139130279497036E138,1.3279615867533290400960133446601505131139135773529E+77
+2.296939338482336E154,2.146234339286729E138,1.5155656826684669775389702686130379487579514632598E+77
+1.717964827353292E154,1.8364794669093787E138,1.3107115729073624262755273613302651224295129698129E+77
+2.2734331712555695E154,2.7786985631933052E138,1.5077908247683329733763454310031116492476843441996E+77
+2.6002514759090857E154,1.685285345205152E138,1.6125295271433282579614905037395666445863925621458E+77
+1.6340348753360928E154,2.165983124050578E138,1.2782937359371252840062824808814182009291340437778E+77
+1.9818511660726223E154,2.343096400425433E138,1.4077823574944468496867993640934852411629249736787E+77
+2.103106210760611E154,1.647444643454262E138,1.4502090231275666160363753385400939874689487591213E+77
+2.505422583425069E154,2.9499525161142467E138,1.5828526726846909113231065377690248388695004693054E+77
+2.5841823619652668E154,2.753961072123544E138,1.6075392256381388384824259337760896541783096771498E+77
+1.9788090550525548E154,1.856618387201154E138,1.4067014804330572694571999827063457472149528996301E+77
+2.6674333283366294E154,2.1588592201259243E138,1.6332278862230554002485381278452409597487221531505E+77
+2.505694005660467E154,2.767104568423107E138,1.5829384086756083077846955902636960950751278389734E+77
+2.17778173577921E154,2.7705852361300355E138,1.4757309157767246439032440846644457306332935521903E+77
+2.063726539762248E154,2.2958473752878785E138,1.4365676245002350455187272132970421707961132521017E+77
+1.56832845156517E154,2.937608586708141E138,1.2523292105373771626288021582911069746221890324145E+77
+1.868311759346738E154,2.9430977800062472E138,1.3668620118163860711769390684765782068806363583242E+77
+2.318739957637079E154,2.2847448042336423E138,1.5227409358249613787586412565005130419937103347242E+77
+2.0760483493522314E154,1.6113061650264947E138,1.4408498705112311504495911350146226091176864675878E+77
+1.766225371812109E154,1.5048601769129825E138,1.3289941203075764618601232756873265324382593455660E+77
+2.1665119878589025E154,2.265229963350782E138,1.4719076016716887520070484009934400499104786462688E+77
+2.52755777949387E154,1.9290147572465096E138,1.5898294812632800968066677875170271078351029499728E+77
+2.280360698212229E154,2.527362637373684E138,1.5100863214439859613066834162587289024589175439933E+77
+1.5633389933508216E154,2.9147279115584247E138,1.2503355523021897395419967345546166538423321413803E+77
+2.6797891497088137E154,1.798458517279449E138,1.6370061544504998905734295491876452411286200523692E+77
+2.2217024604929663E154,2.0424204868377854E138,1.4905376414210298201371040368545677124412522146107E+77
+2.651368459816833E154,2.912247543272382E138,1.6283023244523215396822199093222859215463823469021E+77
+2.4839648123330964E154,2.6501864853973442E138,1.5760599012515662325966126665493758206892634091368E+77
+1.591349547858141E154,1.873564730309309E138,1.2614870383234783980957609807347980201328235758394E+77
+1.636998946380355E154,2.014451361534738E138,1.2794525963787619359354416731473991507319251284108E+77
+2.0543251716665705E154,2.7704190955002384E138,1.4332917259464560142797346650131245980341144184586E+77
+1.7428046873393545E154,2.9618032103412904E138,1.3201532817591125491656270415703442578481160922982E+77
+2.4844003062764626E154,2.0295760212396202E138,1.5761980542674397194444634097824903766744946495763E+77
+1.5481443131311872E154,2.2005395781566852E138,1.2442444748244564373391049274422336644994103258070E+77
+1.9285696826249898E154,2.5485935621200583E138,1.3887295210461215028737738120371607007054876393002E+77
+2.182953537294421E154,2.4892077219618687E138,1.4774821614132676732717727056429188472229439258364E+77
+2.2430315057799687E154,2.563209004861566E138,1.4976753672875737547757408267901233670517592407889E+77
+1.4317121821796738E154,1.6755137656860723E138,1.1965417594800751523509369935131787403488282336199E+77
+1.6810841925090504E154,1.7483876213631218E138,1.2965663085662262321208199891646672694445422766802E+77
+2.5453055553031627E154,2.9621187631696116E138,1.5954013774919347853368189113395790735850071169066E+77
+2.0262732932861942E154,1.6980446389588794E138,1.4234722664267802031477891030287348401868122883943E+77
+2.134798723584321E154,2.746832744266792E138,1.4610950426253321957652263006313189957275142555518E+77
+1.9387023174928182E154,2.714299093019359E138,1.3923729089194526913635761522976386464901429776109E+77
+1.8998197871007164E154,1.529663275606718E138,1.3783395035696816892634054913934830162575596652474E+77
+2.409854527553307E154,2.0468386386703472E138,1.5523706153986899191955371060666968536472038840804E+77
+2.337926317440445E154,2.6135301970029715E138,1.5290278994970775795027880886899987385516473771153E+77
+1.8779957896140248E154,1.998833172513204E138,1.3703998648620865080534719425259897923844370129477E+77
+1.4054944559256395E154,2.6074841936485534E138,1.1855355144092646378200994582857361090460779560135E+77
+2.218748889087122E154,2.5880156209500507E138,1.4895465380736253917216615738548557207119885785718E+77
+1.7837901092499598E154,2.5566381416036415E138,1.3355860546029821679249900222896356079345293689740E+77
+1.8739847234747794E154,1.8979945837406638E138,1.3689356169940131845240308325172279362702965458304E+77
+1.9253431052601193E154,1.5134467416720937E138,1.3875673335950653853033744594747714469341419389424E+77
+1.5519088457946868E154,2.5408855137022825E138,1.2457563348402796961318440332963983587876190825664E+77
+2.0528747333321123E154,2.1949129288385895E138,1.4327856550552537327853569182858674549606917065633E+77
+2.0205680573360322E154,1.6892504562792006E138,1.4214668681809056989254408539824777424250447034705E+77
+2.5831381211248747E154,2.1950414609586476E138,1.6072143979957605137542454574480151122597023608444E+77
+1.4846519260159164E154,2.5507192612876067E138,1.2184629358400347628621359506046713634978995991799E+77
+1.8932295191945175E154,2.973760966449816E138,1.3759467719336085469693234028147508934565008914072E+77
+1.6928338219132205E154,2.6247391405946033E138,1.3010894749836464479157642314890507702215810392333E+77
+1.7670979487519353E154,2.5907290346539968E138,1.3293223644970152838063415066911007241945103733577E+77
+1.5085520712211374E154,2.56134230877782E138,1.2282312775781024018010558734984299915475524573185E+77
+2.235512891286983E154,2.462648911972124E138,1.4951631654394724545633651170015506071476591096060E+77
+1.9299467421947133E154,2.440156981613802E138,1.3892252309091976386061924792034314917995829281421E+77
+1.4964370585925177E154,2.0325439146674806E138,1.2232894418707773079063787580836086196579843523518E+77
+2.170450576242743E154,2.1576210150237053E138,1.4732449138696333350347643641816527323951428210728E+77
+1.721539931299208E154,2.294791995192035E138,1.3120746668155772708329439691074804238259960839224E+77
+2.6341210994385E154,2.9673374111352172E138,1.6229975660605594976919857819579763124621638049762E+77
+2.27511470223837E154,2.9771190503209948E138,1.5083483358423446137661527681363939029913348023434E+77
+1.5589824642832045E154,2.260429288289359E138,1.2485921929450002588125606148116242383956733525569E+77
+1.5720988465635608E154,2.4139151787146938E138,1.2538336598462975869916683339022863525214852641503E+77
+2.1700190912580857E154,2.963056908323949E138,1.4730984662466002341498794799366402800965483922030E+77
+2.1173436425378236E154,1.9426900675100124E138,1.4551094950339042539088726515932188602214474788627E+77
+2.557038072293307E154,2.8919228828544308E138,1.5990741297054703431184362319435416647812172056816E+77
+1.8447323190409128E154,2.030568046438408E138,1.3582092324236766771923976165021656795555812982838E+77
+2.5085365742508126E154,2.7922801695652762E138,1.5838360313652461037363626756556095828797947674365E+77
+1.4483658777386696E154,2.8747861304444807E138,1.2034807342615293952130720086124037237496736234841E+77
+2.1582646638708993E154,2.712936847214093E138,1.4691033537062324562062590105134127605792942151428E+77
+1.7227815855613563E154,2.71587613613776E138,1.3125477460120666451895406108809892082913248309211E+77
+1.8791935287393017E154,2.192294457284726E138,1.3708367987252537583762343477541677505091212967230E+77
+2.2769401591926865E154,2.1118850933843165E138,1.5089533323442069530015168728276843564396072478896E+77
+1.5985186056957248E154,2.3507688363785143E138,1.2643253559490630909546458043401222757122759774075E+77
+2.4778039928175748E154,2.7554673396641333E138,1.5741041874086908914115892969622288424498493932020E+77
+2.4508633747940718E154,2.2134789875896485E138,1.5655233549181155588755413193055673895086860733778E+77
+1.501915155386556E154,2.3780120064471837E138,1.2255264808997626160104646533780880862509171586029E+77
+1.3540919605337654E154,1.6628724206176068E138,1.1636545709675898428913396288653807352799069841226E+77
+1.7844695833302145E154,2.838808156753342E138,1.3358404033903955727450220558651770505583871707323E+77
+1.9712704240594535E154,2.5942836352157146E138,1.4040193816537768060138252352629726578138520904553E+77
+2.5404335293455177E154,2.1421109256497974E138,1.5938737495001032598759361918464444929916360278329E+77
+1.5044800445651598E154,2.6590278123776562E138,1.2265724783171845143035240412646406333567769507451E+77
+2.376474078135673E154,2.256031582959585E138,1.5415816806564850833121179121246328007751699255958E+77
+1.559394865823252E154,2.1504744292203888E138,1.2487573286364537281663537603977263743188629558231E+77
+1.6483348720601048E154,1.627111518820058E138,1.2838749440892227928848551946258923713638949988837E+77
+1.3802957849812013E154,1.728001347604891E138,1.1748599001503121742197125020925785162761965750957E+77
+2.1000273398060858E154,2.8924654218666036E138,1.4491471077175312552995830223849839870286208474032E+77
+1.593113999034636E154,2.5096171111919056E138,1.2621861982428093236945941366954367415907410642529E+77
+2.2524454140027774E154,2.756910893848231E138,1.5008149166378836594188502698485951616210277778401E+77
+1.9095769201128604E154,1.78111182658093E138,1.3818744227001455525057115023097333515995656169717E+77
+2.0760886762295168E154,2.523504279946322E138,1.4408638645720549155667991573574709408622087672856E+77
+2.575085037638067E154,1.5629172927585482E138,1.6047071501174496823644147909424575366075620130868E+77
+1.923156908977609E154,2.864709990143219E138,1.3867793295898267367926990141789305625942726118171E+77
+1.979273584254565E154,1.7994847708211555E138,1.4068665836725831012888019168352985030452015707274E+77
+2.0235039260880596E154,2.7962577626250217E138,1.4224991831590132401452738522422392242771477342994E+77
+2.5876789168753928E154,1.9108932075032884E138,1.6086264068687275506446018118318741392670872688337E+77
+2.424349894128467E154,1.54547347186777E138,1.5570323998326005334201909113048564852058829248969E+77
+1.343867849461189E154,2.370959910929833E138,1.1592531429593749976638048545788829409102848778984E+77
+1.468535480163424E154,2.6677583639621802E138,1.2118314569953299379267197978632367147085102509951E+77
+1.5932742570261917E154,2.1277160585062808E138,1.2622496809372509847860242475601695715840530364046E+77
+2.10365215111703E154,1.7074473057472368E138,1.4503972390752231678608338790855209384807402108054E+77
+2.6019350225951998E154,2.5744972865328678E138,1.6130514630957067094393238029953716079228636359550E+77
+1.8737334434308255E154,2.6435790810441798E138,1.3688438345665387772462770659050478174377656005513E+77
+2.4151115294120573E154,2.537942370643587E138,1.5540629103778448847779056382472749778800944764008E+77
+1.436058187912359E154,2.2244194037749567E138,1.1983564527770355125078460103983203112991193313164E+77
+2.1183841216225338E154,2.9198160012214127E138,1.4554669771666185547302131124757677564207610766431E+77
+2.3072561729663064E154,2.8631324162445376E138,1.5189654943303704523633428560686576980190920065133E+77
+2.5435141773750776E154,2.660555804740314E138,1.5948398594765174278717883110785504008404810218610E+77
+1.946129773232516E154,2.164579102704331E138,1.3950375526244862566061550398958283772235054036044E+77
+2.0378116435378846E154,2.7344100467002358E138,1.4275194021581229908315545170091247286885577366945E+77
+2.06554310767143E154,1.7747815972617895E138,1.4371997452238259208705341337391888514458020434720E+77
+2.0690368041442418E154,2.2958700378529484E138,1.4384146843467088365967999953216379150336201127150E+77
+1.9374714543394823E154,1.8447484729767572E138,1.3919308367657792868814983857379648512348252576587E+77
+1.916692782683302E154,1.890890512460268E138,1.3844467424510420921858117061477588479273874298182E+77
+2.1657723189868144E154,2.109147380013625E138,1.4716563182301819638796486739680169953205368070389E+77
+1.7832327918893338E154,2.0043443123208968E138,1.3353773968018681322683545202289852384758102274556E+77
+1.6513500461956584E154,2.3950086128803197E138,1.2850486551861212161454629665078245574718271257581E+77
+1.3747794861781457E154,1.7993606411040125E138,1.1725099087761032310444996226329484100593184093588E+77
+1.868098827049233E154,2.3620693910600993E138,1.3667841186702577823958898034966015668487936234606E+77
+2.4561847262319596E154,2.450818799590521E138,1.5672219773318519301869913565548225615906002035604E+77
+2.345847462188708E154,1.6824978568691905E138,1.5316159643294099267358031875352203851322729869810E+77
+2.561386727031198E154,1.7659726417294147E138,1.6004332935274741474380710584799734627656069193753E+77
+2.1219679472033228E154,2.5191587282800848E138,1.4566976169415954228231935096593818246410696773199E+77
+1.5821485002898005E154,2.1028166262651E138,1.2578348461899919531550386212548235165000224537872E+77
+2.3017189029990536E154,2.2824175371317278E138,1.5171416885047532927790643319079445331507790592904E+77
+2.5506749241553464E154,2.6295937387572698E138,1.5970832552360401894938695936916209644388227743250E+77
+1.4661665592650748E154,2.1241789048022373E138,1.2108536489869760499402236650503364387724276999086E+77
+1.8124255476083346E154,2.3735584012454493E138,1.3462635505755679545264201709238698428134611794082E+77
+2.2455430438665334E154,2.7401739825530817E138,1.4985136115052588002723738442951241675763218390684E+77
+1.4281974851463535E154,2.8449063971321198E138,1.1950721673381711466035434799119256281093292753366E+77
+2.2162434265843494E154,2.3958348398941214E138,1.4887052853349952468066687560044502624506606927029E+77
+2.13122461767423E154,1.96743648243991E138,1.4598714387487106138475557447064401737773649962618E+77
+1.4337377876061193E154,1.6228822414532828E138,1.1973879018956720028387026642265345809191646610610E+77
+1.997581357718189E154,1.59769809933097E138,1.4133581845088629844291882592021072679536031133991E+77
+1.425306773872042E154,2.0076627044477774E138,1.1938621251518293144971095508119523307047701713394E+77
+1.6161607448955783E154,2.3797159753989677E138,1.2712831096555867089608806379675934527915168422431E+77
+1.652816530092173E154,2.2940195469102396E138,1.2856191232601408972166746091529060622705697199623E+77
+1.5726545150586314E154,1.933748080465975E138,1.2540552280735611816539958547750532382318888170934E+77
+2.4674912006503093E154,2.2128442044817947E138,1.5708250063741376184011003140968050938136096087105E+77
+2.028777030650831E154,2.0423001608425488E138,1.4243514421135084015750051304109050831002959192332E+77
+2.6064943068748547E154,1.9737732749795175E138,1.6144640927796613347926757567330733144093103665978E+77
+1.7041309489290453E154,2.9041845967963375E138,1.3054236664504920671490016158873707160932228964770E+77
+1.9291897699861285E154,2.674801029229796E138,1.3889527601708161547325609601504460767848982479912E+77
+1.431535101269981E154,1.721649954057341E138,1.1964677602300787222261586874546959087415376546106E+77
+2.4220485665073766E154,2.9201465315858086E138,1.5562932135389451933371462272513255510987626933494E+77
+1.341332649860268E154,2.5115720917178206E138,1.1581591643035374520451657922326226806783453648497E+77
+2.282454369026887E154,1.8029051007426233E138,1.5107793912503861319142549016284416550642879759151E+77
+2.6168836732909577E154,2.0565780566552033E138,1.6176784826692101033131779127984510961145762667602E+77
+1.968660977992063E154,2.7172640694691204E138,1.4030897968384144792925217413359740754502259784665E+77
+1.622550560841819E154,1.954202862002734E138,1.2737937669975541293484436570275374832193377868535E+77
+2.382778314384099E154,2.3467030188695475E138,1.5436250562828070567114546091386725115370021875678E+77
+1.569822666574225E154,2.1078687690529078E138,1.2529256428751967818779488931961838146362448080571E+77
+2.5729016906117275E154,2.524078166232572E138,1.6040267113149106636460807748226787399470891506177E+77
+2.055638526054708E154,1.690874820816801E138,1.4337498129222923348445852273442279883697543469629E+77
+1.9314488428987744E154,2.4416886100345343E138,1.3897657510885691102431450965573974617462928159815E+77
+1.8831584463195987E154,2.3634028844165627E138,1.3722822036008478790904890312415806783942408900126E+77
+2.0382417474189147E154,2.5317749585754045E138,1.4276700415078110619101658251489909650867484324317E+77
+1.9051439699153538E154,2.580885988699501E138,1.3802695279963816127517087705875146348644943442434E+77
+2.469553182710233E154,2.6669140746316817E138,1.5714812066042130448589681275980277438380276163828E+77
+1.8628758364136435E154,2.8754942726711212E138,1.3648720952578830425629527783252056085109406945679E+77
+1.842768310477596E154,2.8178715168721686E138,1.3574860258866742435698971134043971375050192688998E+77
+2.2232899146955238E154,1.6466031762439946E138,1.4910700569374746625793888751047589716026631809526E+77
+2.145005375893147E154,1.5505799173617647E138,1.4645836868861905666284064011048538126350218043073E+77
+1.433383080013911E154,1.4990375286724614E138,1.1972397754894009725399118341594609634955205690397E+77
+1.5361086575323117E154,1.4946381337157174E138,1.2393985063458450877012775863105255301999171649410E+77
+2.2271394568103112E154,2.0947098518261283E138,1.4923603642586838156930272506179051124929987928756E+77
+2.2366955276318553E154,1.9831287976777735E138,1.4955586005342136091726008798174887750677881611804E+77
+1.7078309661134934E154,2.1357092207096463E138,1.3068400690648774483914559234408255564304590923081E+77
+2.4409404972452497E154,2.4995042224392086E138,1.5623509520095828723109937632738203446800367846694E+77
+2.633190293978733E154,1.860659229319578E138,1.6227107856850933742147469677224321712901148760161E+77
+2.6563512810075598E154,2.478848849044888E138,1.6298316725992166329178044218605599219908615575336E+77
+1.3796125603538942E154,1.846079833306613E138,1.1745690956065098060567984770545030208804685161048E+77
+1.993251510030203E154,1.7624005962518688E138,1.4118255947638161310417583754483940613499146052385E+77
+2.2477427218863827E154,2.515392020024949E138,1.4992473851524247240574630287462295115236434582234E+77
+1.3452957674773406E154,1.9847523570372756E138,1.1598688578789158834079707972131820262496638061902E+77
+1.53215529893283E154,2.1085630556946743E138,1.2378026090345868698226095382747601090365131751188E+77
+1.7546708906940775E154,2.661639436329943E138,1.3246399098223176232146429454381822467728615426796E+77
+2.292225158004221E154,1.617152117117624E138,1.5140096294291596367605857501522543830992010851580E+77
+1.5513200917787467E154,2.2784162679423994E138,1.2455200085822575670368449700970059828211722656732E+77
+1.3841010048971952E154,2.7325432598244583E138,1.1764782211741939052140734402464853880737906159385E+77
+2.620037894933357E154,2.8860628781574224E138,1.6186531113655443694650807170249455184574433208138E+77
+1.727291084769872E154,2.9217540532082454E138,1.3142644653074480371085988253695560182645304450486E+77
+1.707042961450808E154,1.9181147763941523E138,1.3065385418925873365404684673061715171452600239715E+77
+2.0850399276215127E154,1.873935998061513E138,1.4439667335577758586872334070851196418478050155250E+77
+2.074780344293858E154,1.6454165540015808E138,1.4404097834622820650894507142640377456752856950329E+77
+1.5299222060198804E154,1.7633019965284254E138,1.2369002409329058777459198864826835816283076356752E+77
+2.6363964538141387E154,2.1440400858325352E138,1.6236983875751490348689594778086863174244132669650E+77
+1.851164093178538E154,1.9222374793575318E138,1.3605749127403967112975992929214365373449011523859E+77
+1.455103664684197E154,1.6267906527283745E138,1.2062767778102159979918130286893589025253261242450E+77
+1.3859515483191802E154,1.6855249549977052E138,1.1772644343218648077072018642002329863235445954382E+77
+2.1275244382776794E154,2.1987639199801423E138,1.4586035918911209242647483011121890585869953568338E+77
+1.8951787164512812E154,2.4819914560906233E138,1.3766549010014388490599410244986405105743490835592E+77
+2.3888926404777393E154,1.6855946337573555E138,1.5456042962148298390861927201086176759263018783774E+77
+2.3282323946116313E154,2.9404934447876885E138,1.5258546439984483709840538758127700628051274874970E+77
+2.6509753898751663E154,2.0768234080496388E138,1.6281816206661855537587745478801457201236504353312E+77
+1.6663147948451767E154,2.4437353517685202E138,1.2908581621716527651552449211793944943462993102519E+77
+2.5091482761246663E154,2.3777598472559458E138,1.5840291272968014869833647639248519557831089428528E+77
+2.630627156518907E154,1.868166963328654E138,1.6219208231349973119200015237038541394332630899315E+77
+1.5802271136426174E154,2.1214540201520405E138,1.2570708467077810582633838828712108718091353063273E+77
+2.523321095451858E154,2.178897992150033E138,1.5884964889642841708409493146539944792058510308050E+77
diff --git a/commons-numbers-examples/examples-jmh/pom.xml b/commons-numbers-examples/examples-jmh/pom.xml
index 09053b9a..ce0a888a 100644
--- a/commons-numbers-examples/examples-jmh/pom.xml
+++ b/commons-numbers-examples/examples-jmh/pom.xml
@@ -45,6 +45,13 @@
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-numbers-core</artifactId>
     </dependency>
+    <!-- Required for DD performance benchmark -->
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-numbers-core</artifactId>
+      <type>test-jar</type>
+      <scope>compile</scope>
+    </dependency>
 
     <dependency>
       <groupId>org.apache.commons</groupId>
@@ -110,12 +117,25 @@
     <!-- Disable JDK compatibility check for benchmarking code. Also required since no signature
     projects exist for JDK 9+. -->
     <animal.sniffer.skip>true</animal.sniffer.skip>
+    <!-- Disable moditect as we import o.a.c.numbers.core twice due to the test-jar code. -->
+    <moditect.skip>true</moditect.skip>
+
+    <!-- 
+      NOTE:
+      This module imports a core test artifact and uses Java 9 despite importing
+      only Java 8 code. This can cause the javadoc plugin to fail on some build
+      platforms when run with the package phase so skip to make this module compatible
+      with the default goal. Run using e.g.:
+      mvn javadoc:javadoc -Dmaven.javadoc.skip=false
+    -->
+    <maven.javadoc.skip>true</maven.javadoc.skip>
   </properties>
 
   <build>
     <plugins>
       <plugin>
-        <!-- NOTE: javadoc config must also be set under <reporting> -->
+        <!-- NOTE: javadoc config must also be set under <reporting> 
+            This plugin is skipped by default and must be manually enabled. -->
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
         <configuration>
@@ -138,7 +158,8 @@
   <reporting>
     <plugins>
       <plugin>
-        <!-- NOTE: javadoc config must also be set under <build> -->
+        <!-- NOTE: javadoc config must also be set under <build>
+            This plugin is skipped by default and must be manually enabled. -->
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
         <configuration>
diff --git a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DDPerformance.java b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DDPerformance.java
new file mode 100755
index 00000000..8cefe66a
--- /dev/null
+++ b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DDPerformance.java
@@ -0,0 +1,889 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// License for the Boost continued fraction adaptation:
+
+//  (C) Copyright John Maddock 2006.
+//  Use, modification and distribution are subject to the
+//  Boost Software License, Version 1.0. (See accompanying file
+//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+package org.apache.commons.numbers.examples.jmh.core;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.apache.commons.math3.dfp.DfpField;
+import org.apache.commons.numbers.core.DD;
+import org.apache.commons.numbers.core.DDExt;
+import org.apache.commons.numbers.core.DDMath;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+/**
+ * Executes a benchmark to estimate the speed of double-double extended precision number
+ * implementations.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
+public class DDPerformance {
+
+    /** Static mutable DD implementation. */
+    static final String IMP_DD_STATIC_MUTABLE = "static mutable";
+    /** Static mutable DD implementation. */
+    static final String IMP_DD_STATIC_MUTABLE_FULL_POW = "static mutable full-pow";
+    /** OO immutable DD implementation. */
+    static final String IMP_DD_OO_IMMUTABLE = "OO immutable";
+    /** Full accuracy scaled power implementation. */
+    static final String IMP_ACCURATE_POW_SCALED = "accuratePowScaled";
+    /** Fast scaled power implementation. */
+    static final String IMP_POW_SCALED = "powScaled";
+    /** Low accuracy scaled power implementation base on {@link Math#pow(double, double)}. */
+    static final String IMP_SIMPLE_POW_SCALED = "simplePowScaled";
+
+    /**
+     * Interface for an {@code (double, int) -> double} function.
+     */
+    public interface DoubleIntFunction {
+        /**
+         * Apply the function.
+         *
+         * @param x Argument.
+         * @param n Argument.
+         * @return the result
+         */
+        double apply(double x, int n);
+    }
+
+    /**
+     * Interface for an {@code (DD, int) -> Object} function.
+     */
+    public interface DDIntFunction {
+        /**
+         * Apply the function.
+         *
+         * @param x Argument.
+         * @param n Argument.
+         * @return the result
+         */
+        Object apply(DD x, int n);
+    }
+
+    /**
+     * A {@code (double, int)} tuple.
+     */
+    public static class DoubleInt {
+        /** double value. */
+        private final double x;
+        /** int value. */
+        private final int n;
+
+        /**
+         * @param x double value
+         * @param n int value
+         */
+        DoubleInt(double x, int n) {
+            this.x = x;
+            this.n = n;
+        }
+
+        /**
+         * @return x
+         */
+        double getX() {
+            return x;
+        }
+
+        /**
+         * @return n
+         */
+        int getN() {
+            return n;
+        }
+    }
+
+    /**
+     * A {@code (DD, int)} tuple.
+     */
+    public static class DDInt {
+        /** double value. */
+        private final DD x;
+        /** int value. */
+        private final int n;
+
+        /**
+         * @param x double value
+         * @param n int value
+         */
+        DDInt(DD x, int n) {
+            this.x = x;
+            this.n = n;
+        }
+
+        /**
+         * @return x
+         */
+        DD getX() {
+            return x;
+        }
+
+        /**
+         * @return n
+         */
+        int getN() {
+            return n;
+        }
+    }
+
+    /**
+     * Contains the function to computes the complementary probability {@code P[D_n^+ >= x]}
+     * for the one-sided one-sample Kolmogorov-Smirnov distribution.
+     */
+    @State(Scope.Benchmark)
+    public static class KSMethod {
+        /** The implementation of the function. */
+        @Param({IMP_DD_STATIC_MUTABLE, IMP_DD_STATIC_MUTABLE_FULL_POW, IMP_DD_OO_IMMUTABLE})
+        private String implementation;
+
+        /** The function. */
+        private DoubleIntFunction function;
+
+        /**
+         * Gets the function.
+         *
+         * @return the function
+         */
+        public DoubleIntFunction getFunction() {
+            return function;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            function = createFunction(implementation);
+        }
+
+        /**
+         * Creates the function to evaluate the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param implementation Function implementation
+         * @return the function
+         */
+        static DoubleIntFunction createFunction(String implementation) {
+            if (IMP_DD_STATIC_MUTABLE.equals(implementation)) {
+                return KolmogorovSmirnovDistribution.One::sfMutable;
+            } else if (IMP_DD_STATIC_MUTABLE_FULL_POW.equals(implementation)) {
+                return KolmogorovSmirnovDistribution.One::sfMutableFullPow;
+            } else if (IMP_DD_OO_IMMUTABLE.equals(implementation)) {
+                return KolmogorovSmirnovDistribution.One::sfOO;
+            } else {
+                throw new IllegalStateException("unknown KS method: " + implementation);
+            }
+        }
+
+        /**
+         * Gets the double-double implementations to compute the
+         * one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @return the implementations
+         */
+        static Stream<String> getImplementations() {
+            return Stream.of(IMP_DD_STATIC_MUTABLE, IMP_DD_STATIC_MUTABLE_FULL_POW, IMP_DD_OO_IMMUTABLE);
+        }
+    }
+
+    /**
+     * Contains the data to computes the complementary probability {@code P[D_n^+ >= x]}
+     * for the one-sided one-sample Kolmogorov-Smirnov distribution.
+     */
+    @State(Scope.Benchmark)
+    public static class KSData {
+        // The parameters should be chosen such that the computation takes less than 1 second
+        // thus allowing for repeats in the iteration.
+        // Maintain n*x*x < 372.5 and n*x > 3 (see KSSample for details).
+        // n=10000, values=50, ux=0.15
+        // n=100000, values=5, ux=0.05
+
+        /** The sample size for the KS distribution. This should be below the large N limit of 1000000. */
+        @Param({"1000"})
+        private int n;
+        /** The number of values. */
+        @Param({"500"})
+        private int values;
+        /** The lower limit on x. */
+        @Param({"0.001"})
+        private double lx;
+        /** The upper limit on x. */
+        @Param({"0.5"})
+        private double ux;
+
+        /** The data. */
+        private DoubleInt[] data;
+
+        /**
+         * Gets the data.
+         *
+         * @return the data
+         */
+        public DoubleInt[] getData() {
+            return data;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            data = createData(n, values, lx, ux);
+        }
+
+        /**
+         * Creates the data for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         * The value {@code x} should by in the range [0, 1].
+         *
+         * @param n KS sample size
+         * @param values Number of values
+         * @param lx Lower limit on x
+         * @param ux Upper limit on x
+         * @return the data
+         */
+        static DoubleInt[] createData(int n, int values, double lx, double ux) {
+            assert n > 0 : "Invalid n";
+            assert lx <= ux : "Invalid range";
+            if (values <= 1) {
+                // Single value
+                return new DoubleInt[] {new DoubleInt((lx + ux) * 0.5, n)};
+            }
+            // Create values between the lower and upper range
+            final double inc = (ux - lx) / (values - 1);
+            return IntStream.range(0, values)
+                            .mapToObj(i -> new DoubleInt(lx + inc * i, n))
+                            .toArray(DoubleInt[]::new);
+        }
+    }
+
+    /**
+     * Contains the data to computes the complementary probability {@code P[D_n^+ >= x]}
+     * for the one-sided one-sample Kolmogorov-Smirnov distribution.
+     */
+    @State(Scope.Benchmark)
+    public static class KSSample {
+        /** The sample size for the KS distribution. This should be below the large N limit of 1000000. */
+        @Param({"10000", "100000"})
+        private int n;
+        /**
+         * The KS value (in the range [0, 1].
+         *
+         * <p>Note that the use of the full computation depends on the parameters.
+         * If {@code n*x*x >= 372.5} then the p-value underflows. So large n limits the
+         * usable upper range of x. If {@code n*x <= 3} then a faster computation can be performed
+         * (either Smirnov-Dwass or exact when {@code nx <= 1}).
+         */
+        @Param({"0.01", "0.05"})
+        private double x;
+
+        /**
+         * @return x
+         */
+        double getX() {
+            return x;
+        }
+
+        /**
+         * @return n
+         */
+        int getN() {
+            return n;
+        }
+    }
+
+    /**
+     * Contains the data to compute the double-double operations.
+     */
+    @State(Scope.Benchmark)
+    public static class OperatorData {
+        /** The sample size. */
+        @Param({"1000"})
+        private int n;
+
+        /** The data. */
+        private DD[] data;
+        /** The second data. */
+        private DD[] data2;
+
+        /**
+         * Gets the data.
+         *
+         * @return the data
+         */
+        public DD[] getData() {
+            return data;
+        }
+
+        /**
+         * Gets the second data.
+         *
+         * @return the second data
+         */
+        public DD[] getData2() {
+            return data2;
+        }
+
+        /**
+         * Create the data.
+         */
+        @Setup(Level.Iteration)
+        public void setup() {
+            data = createData(n);
+            data2 = createData(n);
+        }
+
+        /**
+         * Creates data where the high part is approximately uniform in the range [-1, 1).
+         *
+         * @param n sample size
+         * @return the data
+         */
+        static DD[] createData(int n) {
+            UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
+            return IntStream.range(0, n)
+                            .mapToObj(i -> makeSignedDoubleDouble(rng))
+                            .toArray(DD[]::new);
+        }
+    }
+
+    /**
+     * Contains the data to compute the double-double operations.
+     *
+     * <p>Note: This targets the package-private SDD implementation which contains variants
+     * of binary operators.
+     */
+    @State(Scope.Benchmark)
+    public static class BinaryOperatorMethod {
+        /** The implementation of the function. */
+        @Param({"baseline",
+                // Summation of double-double values: (x, xx) + (y, yy)
+                "add", "accurateAdd",
+                // Summation of double values:  (x, xx) + y
+                // Add y to the high part:
+                // (x + y) => (z, zz) => (z, zz + xx)
+                "addDouble", "accurateAddDouble",
+                // Add y to the low part an ignore round-off
+                // (x, xx + y)
+                "twoSum", "fastTwoSum",
+                // Multiplication: (x, xx) * (y, yy)
+                "multiply", "accurateMultiply", "checkedMultiply",
+                "square", "accurateSquare",
+                // Multiplication: (x, xx) * y
+                "multiplyDouble", "accurateMultiplyDouble", "checkedMultiplyDouble",
+                // Division: (x, xx) / (y, yy)
+                "divide", "accurateDivide",
+                // Division: (x, xx) / y
+                "divideDouble",
+                "reciprocal", "accurateReciprocal",
+                "sqrt", "accurateSqrt"})
+        private String implementation;
+
+        /** The function. */
+        private BiFunction<DD, DD, Object> function;
+
+        /**
+         * Gets the function.
+         *
+         * @return the function
+         */
+        public BiFunction<DD, DD, Object> getFunction() {
+            return function;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            function = createFunction(implementation);
+        }
+
+        /**
+         * Creates the function to compute the double-double operation.
+         *
+         * @param implementation Function implementation
+         * @return the function
+         */
+        static BiFunction<DD, DD, Object> createFunction(String implementation) {
+            if ("baseline".equals(implementation)) {
+                return (a, b) -> DD.of(a.hi());
+            } else if ("add".equals(implementation)) {
+                return DD::add;
+            } else if ("accurateAdd".equals(implementation)) {
+                return DDExt::add;
+            } else if ("addDouble".equals(implementation)) {
+                return (a, b) -> a.add(b.hi());
+            } else if ("accurateAddDouble".equals(implementation)) {
+                return (a, b) -> DDExt.add(a, b.hi());
+            } else if ("twoSum".equals(implementation)) {
+                // twoSum is not public in DD, use SDD
+                return (a, b) -> SDD.twoSum(a.hi(), a.lo() + b.hi(), SDD.create());
+            } else if ("fastTwoSum".equals(implementation)) {
+                // fastTwoSum is not public in DD, use SDD
+                return (a, b) -> SDD.fastTwoSum(a.hi(), a.lo() + b.hi(), SDD.create());
+            } else if ("multiply".equals(implementation)) {
+                return DD::multiply;
+            } else if ("accurateMultiply".equals(implementation)) {
+                return DDExt::multiply;
+            } else if ("checkedMultiply".equals(implementation)) {
+                // DD is always unchecked, use SDD
+                return (a, b) -> SDD.multiply(a.hi(), a.lo(), b.hi(), b.lo(), SDD.create());
+            } else if ("square".equals(implementation)) {
+                return (a, b) -> a.square();
+            } else if ("accurateSquare".equals(implementation)) {
+                return (a, b) -> DDExt.square(a);
+            } else if ("multiplyDouble".equals(implementation)) {
+                return (a, b) -> a.multiply(b.hi());
+            } else if ("accurateMultiplyDouble".equals(implementation)) {
+                return (a, b) -> DDExt.multiply(a, b.hi());
+            } else if ("checkedMultiplyDouble".equals(implementation)) {
+                // DD is always unchecked, use SDD
+                return (a, b) -> SDD.multiply(a.hi(), a.lo(), b.hi(), SDD.create());
+            } else if ("divide".equals(implementation)) {
+                return DD::divide;
+            } else if ("accurateDivide".equals(implementation)) {
+                return DDExt::divide;
+            } else if ("divideDouble".equals(implementation)) {
+                return (a, b) -> a.divide(b.hi());
+            } else if ("reciprocal".equals(implementation)) {
+                return (a, b) -> a.reciprocal();
+            } else if ("accurateReciprocal".equals(implementation)) {
+                return (a, b) -> DDExt.reciprocal(a);
+            } else if ("sqrt".equals(implementation)) {
+                return (a, b) -> a.sqrt();
+            } else if ("accurateSqrt".equals(implementation)) {
+                return (a, b) -> DDExt.sqrt(a);
+            } else {
+                throw new IllegalStateException("unknown binary operator: " + implementation);
+            }
+        }
+    }
+
+    /**
+     * Contains the data to computes the power function {@code (x, xx)^n}.
+     */
+    @State(Scope.Benchmark)
+    public static class PowSample {
+        // The parameters should be chosen such that the pow computation does not overflow
+
+        /** The number of values. */
+        @Param({"500"})
+        private int values;
+        /** The lower limit on n. */
+        @Param({"-1022"})
+        private int lower;
+        /** The upper limit on n. */
+        @Param({"1022"})
+        private int upper;
+
+        /** The data. */
+        private DDInt[] data;
+
+        /**
+         * Gets the data.
+         *
+         * @return the data
+         */
+        public DDInt[] getData() {
+            return data;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            if (lower > upper) {
+                throw new IllegalStateException(String.format("invalid range: %s to %s", lower, upper));
+            }
+            if (!Double.isFinite(Math.pow(0.5, lower))) {
+                throw new IllegalStateException("lower overflow 0.5^" + lower);
+            }
+            if (!Double.isFinite(Math.pow(0.5, upper))) {
+                throw new IllegalStateException("upper overflow 0.5^" + upper);
+            }
+            if (!Double.isFinite(Math.pow(2, lower))) {
+                throw new IllegalStateException("lower overflow 2^" + lower);
+            }
+            if (!Double.isFinite(Math.pow(2, upper))) {
+                throw new IllegalStateException("upper overflow 2^" + upper);
+            }
+            UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
+            data = IntStream.range(0, values)
+                            .mapToObj(i -> {
+                                // DD in +/- [1, 2)
+                                DD dd = makeSignedNormalDoubleDouble(rng);
+                                if (rng.nextBoolean()) {
+                                    // Change to +/- [0.5, 2)
+                                    dd = dd.scalb(-1);
+                                }
+                                return new DDInt(dd, rng.nextInt(lower, upper));
+                            }).toArray(DDInt[]::new);
+        }
+    }
+
+    /**
+     * Contains the scaled power operation.
+     */
+    @State(Scope.Benchmark)
+    public static class PowMethod {
+        /** The implementation of the function. */
+        @Param({"pow", IMP_POW_SCALED, IMP_ACCURATE_POW_SCALED, IMP_SIMPLE_POW_SCALED})
+        private String implementation;
+
+        /** The function. */
+        private DDIntFunction function;
+
+        /**
+         * Gets the function.
+         *
+         * @return the function
+         */
+        public DDIntFunction getFunction() {
+            return function;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            function = createFunction(implementation);
+        }
+
+        /**
+         * Creates the function to evaluate the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param implementation Function implementation
+         * @return the function
+         */
+        static DDIntFunction createFunction(String implementation) {
+            if ("pow".equals(implementation)) {
+                return DD::pow;
+            } else if (IMP_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> x.pow(n, exp);
+            } else if (IMP_ACCURATE_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> DDMath.pow(x, n, exp);
+            } else if (IMP_SIMPLE_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> DDExt.simplePowScaled(x.hi(), x.lo(), n, exp);
+            } else {
+                throw new IllegalStateException("unknown pow: " + implementation);
+            }
+        }
+    }
+
+    /**
+     * Contains the data to computes the scaled power function {@code (x, xx)^n}.
+     * This method returns result with a separate scale and supports powers beyond the range
+     * of a {@code double}.
+     *
+     * <p>Data have been taken from the test cases in the {@code o.a.c.statistics.ext} package
+     * as these do not overflow BigDecimal using exponents in the range [50000000, 60000000).
+     */
+    @State(Scope.Benchmark)
+    public static class PowScaledSample {
+        /** The power exponent. This should be below the limit of BigDecimal (0 through 999999999).
+         * Note that BigDecimal is too slow for larger powers for micro-benchmarking as run times
+         * can be in seconds. */
+        @Param({"1000", "10000"})
+        private int n;
+        /** The high part of the value. */
+        @Param({"1.4146512942500389",
+            //"1.4092258370859025"
+            })
+        private double x;
+
+        /** The value. */
+        private DD dd;
+
+        /**
+         * @return (x, xx)
+         */
+        DD getDD() {
+            return dd;
+        }
+
+        /**
+         * @return n
+         */
+        int getN() {
+            return n;
+        }
+
+        /**
+         * Create the data.
+         */
+        @Setup(Level.Iteration)
+        public void setup() {
+            // Create a random set of round-off bits.
+            // The roundoff must be < 0.5 ULP of the value.
+            // Generate using +/- [0.25, 0.5) ULP.
+            final double xx = 0.25 * Math.ulp(x) * makeSignedNormalDouble(ThreadLocalRandom.current().nextLong());
+            dd = DD.ofSum(x, xx);
+        }
+    }
+
+    /**
+     * Contains the scaled power operation.
+     */
+    @State(Scope.Benchmark)
+    public static class PowScaledMethod {
+        /** The implementation of the function. */
+        @Param({IMP_POW_SCALED, IMP_ACCURATE_POW_SCALED, IMP_SIMPLE_POW_SCALED, "BigDecimal", "Dfp"})
+        private String implementation;
+
+        /** The function. */
+        private DDIntFunction function;
+
+        /**
+         * Gets the function.
+         *
+         * @return the function
+         */
+        public DDIntFunction getFunction() {
+            return function;
+        }
+
+        /**
+         * Create the function.
+         */
+        @Setup
+        public void setup() {
+            function = createFunction(implementation);
+        }
+
+        /**
+         * Creates the function to evaluate the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param implementation Function implementation
+         * @return the function
+         */
+        static DDIntFunction createFunction(String implementation) {
+            if (IMP_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> x.pow(n, exp);
+            } else if (IMP_ACCURATE_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> DDMath.pow(x, n, exp);
+            } else if (IMP_SIMPLE_POW_SCALED.equals(implementation)) {
+                final long[] exp = {0};
+                return (x, n) -> DDExt.simplePowScaled(x.hi(), x.lo(), n, exp);
+            } else if ("BigDecimal".equals(implementation)) {
+                return (x, n) -> new BigDecimal(x.hi()).add(new BigDecimal(x.lo())).pow(n, MathContext.DECIMAL128);
+            } else if ("Dfp".equals(implementation)) {
+                final DfpField df = new DfpField(MathContext.DECIMAL128.getPrecision());
+                return (x, n) -> df.newDfp(x.hi()).add(x.lo()).pow(n);
+            } else {
+                throw new IllegalStateException("unknown pow scaled: " + implementation);
+            }
+        }
+    }
+
+    /**
+     * Creates a signed double in the range {@code [-1, 1)}. The magnitude is sampled evenly from the
+     * 2<sup>54</sup> dyadic rationals in the range.
+     *
+     * <p>Note: This method will not return samples for both -0.0 and 0.0.
+     *
+     * @param bits the bits
+     * @return the double
+     */
+    private static double makeSignedDouble(long bits) {
+        // Use the upper 54 bits on the assumption they are more random.
+        // The sign bit is maintained by the signed shift.
+        // The next 53 bits generates a magnitude in the range [0, 2^53) or [-2^53, 0).
+        return (bits >> 10) * 0x1.0p-53d;
+    }
+
+    /**
+     * Creates a normalized double in the range {@code [1, 2)} with a random sign. The
+     * magnitude is sampled evenly from the 2<sup>52</sup> dyadic rationals in the range.
+     *
+     * @param bits Random bits.
+     * @return the double
+     */
+    private static double makeSignedNormalDouble(long bits) {
+        // Combine an unbiased exponent of 0 with the 52 bit mantissa and a random sign
+        // bit
+        return Double.longBitsToDouble((1023L << 52) | (bits >>> 12) | (bits << 63));
+    }
+
+    /**
+     * Creates a double-double approximately uniformly distributed in the range {@code [-1, 1)}.
+     *
+     * @param rng Source of randomness.
+     * @return the double-double
+     */
+    private static DD makeSignedDoubleDouble(UniformRandomProvider rng) {
+        // Uniform in [-1, 1) using increments of 2^-53
+        double x = makeSignedDouble(rng.nextLong());
+        // The roundoff must be < 0.5 ULP of the value.
+        // Generate using +/- [0.25, 0.5) ULP.
+        final double xx = 0.25 * Math.ulp(x) * makeSignedNormalDouble(rng.nextLong());
+        return DD.ofSum(x, xx);
+    }
+
+    /**
+     * Creates a normalized double-double in the range {@code [1, 2)} with a random sign.
+     *
+     * @param rng Source of randomness.
+     * @return the double-double
+     */
+    private static DD makeSignedNormalDoubleDouble(UniformRandomProvider rng) {
+        final double x = makeSignedNormalDouble(rng.nextLong());
+        // The roundoff must be < 0.5 ULP of the value.
+        // Math.ulp(1.0) = 2^-52.
+        // Generate using +/- [0.25, 0.5) ULP.
+        final double xx = 0x1.0p-54 * makeSignedNormalDouble(rng.nextLong());
+        return DD.ofSum(x, xx);
+    }
+
+    /**
+     * Apply the function to all the numbers.
+     *
+     * @param fun Function.
+     * @param data Data.
+     * @param bh Data sink.
+     */
+    private static void apply(DoubleIntFunction fun, DoubleInt[] data, Blackhole bh) {
+        for (final DoubleInt d : data) {
+            bh.consume(fun.apply(d.getX(), d.getN()));
+        }
+    }
+
+    /**
+     * Apply the function to all the numbers.
+     *
+     * @param fun Function.
+     * @param data Data.
+     * @param data2 Second data.
+     * @param bh Data sink.
+     */
+    private static void apply(BiFunction<DD, DD, Object> fun, DD[] data, DD[] data2, Blackhole bh) {
+        for (int i = 0; i < data.length; i++) {
+            bh.consume(fun.apply(data[i], data2[i]));
+        }
+    }
+
+    /**
+     * Apply the function to all the numbers.
+     *
+     * @param fun Function.
+     * @param data Data.
+     * @param bh Data sink.
+     */
+    private static void apply(DDIntFunction fun, DDInt[] data, Blackhole bh) {
+        for (int i = 0; i < data.length; i++) {
+            bh.consume(fun.apply(data[i].getX(), data[i].getN()));
+        }
+    }
+
+    // Benchmark methods.
+    // Benchmarks use function references to perform different operations on the numbers.
+
+    /**
+     * Benchmark a range of the KS function.
+     *
+     * @param method Test method.
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void ksRange(KSMethod method, KSData data, Blackhole bh) {
+        apply(method.getFunction(), data.getData(), bh);
+    }
+
+    /**
+     * Benchmark a sample of the KS function.
+     *
+     * @param method Test method.
+     * @param data Test data.
+     * @return the sample value
+     */
+    @Benchmark
+    public double ksSample(KSMethod method, KSSample data) {
+        return method.getFunction().apply(data.getX(), data.getN());
+    }
+
+    /**
+     * Benchmark a sample of the KS function.
+     *
+     * @param method Test method.
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void binaryOperator(BinaryOperatorMethod method, OperatorData data, Blackhole bh) {
+        apply(method.getFunction(), data.getData(), data.getData2(), bh);
+    }
+
+    /**
+     * Benchmark a sample of the KS function.
+     *
+     * @param method Test method.
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void pow(PowMethod method, PowSample data, Blackhole bh) {
+        apply(method.getFunction(), data.getData(), bh);
+    }
+
+    /**
+     * Benchmark a sample of the KS function.
+     *
+     * @param method Test method.
+     * @param data Test data.
+     * @return the result
+     */
+    @Benchmark
+    public Object powScaled(PowScaledMethod method, PowScaledSample data) {
+        return method.getFunction().apply(data.getDD(), data.getN());
+    }
+}
diff --git a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DoublePrecision.java b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DoublePrecision.java
index 53d6036f..c364f654 100644
--- a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DoublePrecision.java
+++ b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/DoublePrecision.java
@@ -204,7 +204,7 @@ final class DoublePrecision {
         // Compute scaling by multiplication so we can scale both together.
         // If a single multiplication to a normal number then handle here.
         if (scale <= 2046 && scale > 0) {
-            // Convert to a normalised power of 2
+            // Convert to a normalized power of 2
             final double d = Double.longBitsToDouble(((long) scale) << 52);
             z *= d;
             zz *= d;
@@ -253,7 +253,7 @@ final class DoublePrecision {
     }
 
     /**
-     * Gets the normalised fraction in the range [0.5, 1).
+     * Gets the normalized fraction in the range [0.5, 1).
      *
      * @param bits the bits
      * @return the exponent
diff --git a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/KolmogorovSmirnovDistribution.java b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/KolmogorovSmirnovDistribution.java
new file mode 100644
index 00000000..c57fc507
--- /dev/null
+++ b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/core/KolmogorovSmirnovDistribution.java
@@ -0,0 +1,706 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.examples.jmh.core;
+
+import org.apache.commons.numbers.core.DD;
+import org.apache.commons.numbers.core.DDMath;
+
+/**
+ * Computes the complementary probability for the one-sample Kolmogorov-Smirnov distribution.
+ *
+ * <p>This class has been extracted from {@code o.a.c.statistics.inference}. It is a subset of
+ * the computations required for the KS distribution.
+ *
+ * @since 1.2
+ */
+final class KolmogorovSmirnovDistribution {
+    /** No instances. */
+    private KolmogorovSmirnovDistribution() {}
+
+    /**
+     * Computes the complementary probability {@code P[D_n^+ >= x]} for the one-sided
+     * one-sample Kolmogorov-Smirnov distribution.
+     *
+     * <pre>
+     * D_n^+ = sup_x {CDF_n(x) - F(x)}
+     * </pre>
+     *
+     * <p>where {@code n} is the sample size; {@code CDF_n(x)} is an empirical
+     * cumulative distribution function; and {@code F(x)} is the expected
+     * distribution. The computation uses Smirnov's stable formula:
+     *
+     * <pre>
+     *                   floor(n(1-x)) (n) ( j     ) (j-1)  (         j ) (n-j)
+     * P[D_n^+ >= x] = x     Sum       ( ) ( - + x )        ( 1 - x - - )
+     *                       j=0       (j) ( n     )        (         n )
+     * </pre>
+     *
+     * <p>Computing using logs is not as accurate as direct multiplication when n is large.
+     * However the terms are very large and small. Multiplication uses a scaled representation
+     * with a separate exponent term to support the extreme range. Extended precision
+     * representation of the numbers reduces the error in the power terms. Details in
+     * van Mulbregt (2018).
+     *
+     * <p>
+     * References:
+     * <ol>
+     * <li>
+     * van Mulbregt, P. (2018).
+     * <a href="https://doi.org/10.48550/arxiv.1802.06966">Computing the Cumulative Distribution Function and Quantiles of the One-sided Kolmogorov-Smirnov Statistic</a>
+     * arxiv:1802.06966.
+     * <li>Magg &amp; Dicaire (1971).
+     * <a href="https://doi.org/10.1093/biomet/58.3.653">On Kolmogorov-Smirnov Type One-Sample Statistics</a>
+     * Biometrika 58.3 pp. 653–656.
+     * </ol>
+     *
+     * @since 1.1
+     */
+    static final class One {
+        /** "Very large" n to use a asymptotic limiting form.
+         * [1] suggests 1e12 but this is reduced to avoid excess
+         * computation time. */
+        private static final int VERY_LARGE_N = 1000000;
+        /** Maximum number of term for the Smirnov-Dwass algorithm. */
+        private static final int SD_MAX_TERMS = 3;
+        /** Minimum sample size for the Smirnov-Dwass algorithm. */
+        private static final int SD_MIN_N = 8;
+        /** Number of bits of precision in the sum of terms Aj.
+         * This does not have to be the full 106 bits of a double-double as the final result
+         * is used as a double. The terms are represented as fractions with an exponent:
+         * <pre>
+         *  Aj = 2^b * f
+         *  f of sum(A) in [0.5, 1)
+         *  f of Aj in [0.25, 2]
+         * </pre>
+         * <p>The terms can be added if their exponents overlap. The bits of precision must
+         * account for the extra range of the fractional part of Aj by 1 bit. Note that
+         * additional bits are added to this dynamically based on the number of terms. */
+        private static final int SUM_PRECISION_BITS = 53;
+        /** Number of bits of precision in the sum of terms Aj.
+         * For Smirnov-Dwass we use the full 106 bits of a double-double due to the summation
+         * of terms that cancel. Account for the extra range of the fractional part of Aj by 1 bit. */
+        private static final int SD_SUM_PRECISION_BITS = 107;
+
+        /**
+         * Defines a scaled power function.
+         */
+        private interface StaticScaledPower {
+            /**
+             * Compute the number {@code x} raised to the power {@code n}.
+             *
+             * <p>The value is returned as fractional {@code f} and integral
+             * {@code 2^exp} components.
+             * <pre>
+             * (x+xx)^n = (f+ff) * 2^exp
+             * </pre>
+             *
+             * @param x High part of x.
+             * @param xx Low part of x.
+             * @param n Power.
+             * @param f Fraction part.
+             * @return Power of two scale factor (integral exponent).
+             * @see SDD#frexp(double, double, SDD)
+             * @see SDD#fastPowScaled(double, double, int, SDD)
+             * @see SDD#powScaled(double, double, int, SDD)
+             */
+            long pow(double x, double xx, int n, SDD f);
+        }
+
+        /**
+         * Defines a scaled power function.
+         */
+        private interface ScaledPower {
+            /**
+             * Compute the number {@code x} raised to the power {@code n}.
+             *
+             * <p>The value is returned as fractional {@code f} and integral
+             * {@code 2^exp} components.
+             * <pre>
+             * (x+xx)^n = (f+ff) * 2^exp
+             * </pre>
+             *
+             * @param x x.
+             * @param n Power.
+             * @param exp Power of two scale factor (integral exponent).
+             * @return Fraction part.
+             * @see DD#frexp(int[])
+             * @see DD#pow(int, long[])
+             */
+            DD pow(DD x, int n, long[] exp);
+        }
+
+        /**
+         * Defines an addition of two double-double numbers.
+         */
+        private interface StatisDDAdd {
+            /**
+             * Compute the sum of {@code (x,xx)} and {@code (y,yy)}.
+             *
+             * @param x High part of x.
+             * @param xx Low part of x.
+             * @param y High part of y.
+             * @param yy Low part of y.
+             * @param s Sum.
+             * @return the sum
+             * @see SDD#add(double, double, double, double, SDD)
+             */
+            SDD add(double x, double xx, double y, double yy, SDD s);
+        }
+
+        /** No instances. */
+        private One() {}
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param x Statistic.
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         */
+        static double sfMutable(double x, int n) {
+            final double p = sfExact(x, n);
+            if (p >= 0) {
+                return p;
+            }
+            // Note: This is not referring to N = floor(n*x).
+            // Here n is the sample size and a suggested limit 10^12 is noted on pp.15 in [1].
+            // This uses a lower threshold where the full computation takes ~ 1 second.
+            if (n > VERY_LARGE_N) {
+                return sfAsymptotic(x, n);
+            }
+            return sfSDD(x, n, false);
+        }
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param x Statistic.
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         */
+        static double sfMutableFullPow(double x, int n) {
+            final double p = sfExact(x, n);
+            if (p >= 0) {
+                return p;
+            }
+            // Note: This is not referring to N = floor(n*x).
+            // Here n is the sample size and a suggested limit 10^12 is noted on pp.15 in [1].
+            // This uses a lower threshold where the full computation takes ~ 1 second.
+            if (n > VERY_LARGE_N) {
+                return sfAsymptotic(x, n);
+            }
+            return sfSDD(x, n, true);
+        }
+
+        /**
+         * Calculates exact cases for the complementary probability
+         * {@code P[D_n^+ >= x]} the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * <p>Exact cases handle x not in [0, 1]. It is assumed n is positive.
+         *
+         * @param x Statistic.
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         */
+        private static double sfExact(double x, int n) {
+            if (n * x * x >= 372.5 || x >= 1) {
+                // p would underflow, or x is out of the domain
+                return 0;
+            }
+            if (x <= 0) {
+                // edge-of, or out-of, the domain
+                return 1;
+            }
+            if (n == 1) {
+                return x;
+            }
+            // x <= 1/n
+            // [1] Equation (33)
+            final double nx = n * x;
+            if (nx <= 1) {
+                // 1 - x (1+x)^(n-1): here x may be small so use log1p
+                return 1 - x * Math.exp((n - 1) * Math.log1p(x));
+            }
+            // 1 - 1/n <= x < 1
+            // [1] Equation (16)
+            if (n - 1 <= nx) {
+                // (1-x)^n: here x > 0.5 and 1-x is exact
+                return Math.pow(1 - x, n);
+            }
+            return -1;
+        }
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * <p>Computes the result using the asymptotic formula Eq 5 in [1].
+         *
+         * @param x Statistic.
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         */
+        private static double sfAsymptotic(double x, int n) {
+            // Magg & Dicaire (1971) limiting form
+            return Math.exp(-Math.pow(6.0 * n * x + 1, 2) / (18.0 * n));
+        }
+
+        /**
+         * Compute exactly {@code x = (k + alpha) / n} with {@code k} an integer and
+         * {@code alpha in [0, 1)}. Note that {@code k ~ floor(nx)} but may be rounded up
+         * if {@code alpha -> 1} within working precision.
+         *
+         * <p>This computation is a significant source of increased error if performed in
+         * 64-bit arithmetic. Although the value alpha is only used for the PDF computation
+         * a value of {@code alpha == 0} indicates the final term of the SF summation can be
+         * dropped due to the cancellation of a power term {@code (x + j/n)} to zero with
+         * {@code x = (n-j)/n}. That is if {@code alpha == 0} then x is the fraction {@code k/n}
+         * and one Aj term is zero.
+         *
+         * @param n Sample size.
+         * @param x Statistic.
+         * @param z Used for computation. Return {@code alpha} in the high part.
+         * @return k
+         */
+        private static int splitX(int n, double x, SDD z) {
+            // Described on page 14 in van Mulbregt [1].
+            // nx = U+V (exact)
+            SDD.twoProd(n, x, z);
+            final double u = z.hi();
+            final double v = z.lo();
+            // Integer part of nx is *almost* the integer part of U.
+            // Compute k = floor((U,V)) (changed from the listing of floor(U)).
+            int k = (int) Math.floor(u);
+            // Incorporate the round-off of u in the floor
+            if (k == u) {
+                // u is an integer. If v < 0 then the floor is 1 lower.
+                k += v < 0 ? -1 : 0;
+            }
+            // nx = k + ((U - k) + V) = k + (U1 + V1)
+            SDD.fastAdd(u, v, -k, z);
+            // alpha = (U1, V1) = z
+            // alpha is in [0, 1) in double-double precision.
+            // Ensure the high part is in [0, 1) (i.e. in double precision).
+            if (z.hi() == 1) {
+                // Here alpha is ~ 1.0-eps.
+                // This occurs when x ~ j/n and n is large.
+                k += 1;
+                SDD.set(0, z);
+            }
+            return k;
+        }
+
+        /**
+         * Returns {@code floor(log2(n))}.
+         *
+         * @param n Value.
+         * @return approximate log2(n)
+         */
+        private static int log2(int n) {
+            return 31 - Integer.numberOfLeadingZeros(n);
+        }
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * @param x Statistic.
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         */
+        static double sfOO(double x, int n) {
+            final double p = sfExact(x, n);
+            if (p >= 0) {
+                return p;
+            }
+            // Note: This is not referring to N = floor(n*x).
+            // Here n is the sample size and a suggested limit 10^12 is noted on pp.15 in [1].
+            // This uses a lower threshold where the full computation takes ~ 1 second.
+            if (n > VERY_LARGE_N) {
+                return sfAsymptotic(x, n);
+            }
+            return sfDD(x, n);
+        }
+
+        // @CHECKSTYLE: stop MethodLength
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * <p>Computes the result using double-double arithmetic. The power function can
+         * use a fast approximation or a full power computation.
+         *
+         * <p>This function is safe for {@code x > 1/n}. When {@code x} approaches
+         * sub-normal then division or multiplication by x can under/overflow. The case of
+         * {@code x < 1/n} can be computed in {@code sfExact}.
+         *
+         * @param x Statistic (typically in (1/n, 1 - 1/n)).
+         * @param n Sample size (assumed to be positive).
+         * @param fullPow If {@code true} compute using a full accuracy pow function
+         * @return \(P(D_n^+ &ge; x)\)
+         * @see SDD#fastPowScaled(double, double, int, SDD)
+         * @see SDD#powScaled(double, double, int, SDD)
+         */
+        private static double sfSDD(double x, int n, boolean fullPow) {
+            // Compute only the SF using Algorithm 1 pp 12.
+            // Only require 1 double-double for all intermediate computations.
+            final SDD z = SDD.create();
+
+            // Compute: k = floor(n*x), alpha = nx - k; x = (k+alpha)/n with 0 <= alpha < 1
+            final int k = splitX(n, x, z);
+            final double alpha = z.hi();
+
+            // Choose the algorithm:
+            // Eq (13) Smirnov/Birnbaum-Tingey; or Smirnov/Dwass Eq (31)
+            // Eq. 13 sums j = 0 : floor( n(1-x) )  = n - 1 - floor(nx) iff alpha != 0; else n - floor(nx)
+            // Eq. 31 sums j = ceil( n(1-x) ) : n   = n - floor(nx)
+            // Drop a term term if x = (n-j)/n. Equates to shifting the floor* down and ceil* up:
+            // Eq. 13 N = floor*( n(1-x) ) = n - k - ((alpha!=0) ? 1 : 0) - ((alpha==0) ? 1 : 0)
+            // Eq. 31 N = n - ceil*( n(1-x) ) = k - ((alpha==0) ? 1 : 0)
+            // Where N is the number of terms - 1. This differs from Algorithm 1 by dropping
+            // a SD term when it should be zero (to working precision).
+            final int regN = n - k - 1;
+            final int sdN = k - ((alpha == 0) ? 1 : 0);
+
+            // SD : Figure 3 (c) (pp. 6)
+            // Terms Aj (j = n -> 0) have alternating signs through the range and may involve
+            // numbers much bigger than 1 causing cancellation; magnitudes increase then decrease.
+            // Section 3.3: Extra digits of precision required
+            // grows like Order(sqrt(n)). E.g. sf=0.7 (x ~ 0.4/sqrt(n)) loses 8 digits.
+            //
+            // Regular : Figure 3 (a, b)
+            // Terms Aj can have similar magnitude through the range; when x >= 1/sqrt(n)
+            // the final few terms can be magnitudes smaller and could be ignored.
+            // Section 3.4: As x increases the magnitude of terms becomes more peaked,
+            // centred at j = (n-nx)/2, i.e. 50% of the terms.
+            //
+            // As n -> inf the sf for x = k/n agrees with the asymptote Eq 5 in log2(n) bits.
+            //
+            // Figure 4 has lines at x = 1/n and x = 3/sqrt(n).
+            // Point between is approximately x = 4/n, i.e. nx < 4 : k <= 3.
+            // If faster when x < 0.5 and requiring nx ~ 4 then requires n >= 8.
+            //
+            // Note: If SD accuracy scales with sqrt(n) then we could use 1 / sqrt(n).
+            // That threshold is always above 4 / n when n is 16 (4/n = 1/sqrt(n) : n = 4^2).
+            // So the current thresholds are conservative.
+            boolean sd = false;
+            if (sdN < regN) {
+                // Here x < 0.5 and SD has fewer terms
+                // Always choose when we only have one additional term (i.e x < 2/n)
+                sd = sdN <= 1;
+                // Otherwise when x < 4 / n
+                sd |= sdN <= SD_MAX_TERMS && n >= SD_MIN_N;
+            }
+
+            final int maxN = sd ? sdN : regN;
+
+            // Note: if N > "very large" use the asymptotic approximation.
+            // Currently this check is done on n (sample size) in the calling function.
+            // This provides a monotonic p-value for all x with the same n.
+
+            // Configure the algorithm.
+            // The error of double-double addition and multiplication is low (< 2^-102).
+            // The error in Aj is mainly from the power function.
+            // fastPow error is around 2^-52, pow error is ~ 2^-70 or lower.
+            // Smirnoff-Dwass has a sum of terms that cancel and requires higher precision.
+            // SD has only a few terms. Use a high accuracy power.
+            StaticScaledPower fpow = sd || fullPow ? SDD::powScaled : SDD::fastPowScaled;
+            // SD requires a more precise summation using all the terms that can be added.
+            // For the regular summation we must sum at least 50% of the terms. The number
+            // of bits required to sum remaining terms of the same magnitude is log2(N/2).
+            // These guards bits are conservative and > ~99% of terms are typically used.
+            final StatisDDAdd fadd = sd ? SDD::add : SDD::fastAdd;
+            final int sumBits = sd ? SD_SUM_PRECISION_BITS : SUM_PRECISION_BITS + log2(maxN >> 1);
+
+            // Working variable for the exponent of scaled values
+            long e;
+
+            // Compute A0. The terms Aj may over/underflow.
+            // This is handled by maintaining the sum(Aj) using a fractional representation.
+            if (sd) {
+                // A0 = (1+x)^(n-1)
+                SDD.fastTwoSum(1, x, z);
+                e = fpow.pow(z.hi(), z.lo(), n - 1, z);
+            } else {
+                // A0 = (1-x)^n / x
+                SDD.fastTwoSum(1, -x, z);
+                e = fpow.pow(z.hi(), z.lo(), n, z);
+                // x in (1/n, 1 - 1/n) so the divide of the fraction is safe
+                SDD.divide(z.hi(), z.lo(), x, 0, z);
+                e += SDD.frexp(z.hi(), z.lo(), z);
+            }
+
+            // sum(Aj) maintained as 2^e * f with f in [0.5, 1)
+            final SDD sum = z.copy();
+            long esum = e;
+            // Binomial coefficient c(n, j) maintained as 2^e * f with f in [1, 2)
+            // This value is integral but maintained to limited precision
+            final SDD c = SDD.create(1);
+            long ec = 0;
+            for (int i = 1; i <= maxN; i++) {
+                // c(n, j) = c(n, j-1) * (n-j+1) / j
+                SDD.uncheckedDivide(n - i + 1, i, z);
+                SDD.uncheckedMultiply(c.hi(), c.lo(), z.hi(), z.lo(), c);
+                // Here we maintain c in [1, 2) to restrict the scaled Aj term to [0.25, 2].
+                final int b = Math.getExponent(c.hi());
+                if (b != 0) {
+                    SDD.ldexp(c.hi(), c.lo(), -b, c);
+                    ec += b;
+                }
+                // Compute Aj
+                final int j = sd ? n - i : i;
+                // Algorithm 4 pp. 27
+                // S = ((j/n) + x)^(j-1)
+                // T = ((n-j)/n - x)^(n-j)
+                SDD.uncheckedDivide(j, n, z);
+                SDD.fastAdd(z.hi(), z.lo(), x, z);
+                final long es = fpow.pow(z.hi(), z.lo(), j - 1, z);
+                final double s = z.hi();
+                final double ss = z.lo();
+                SDD.uncheckedDivide(n - j, n, z);
+                SDD.fastAdd(z.hi(), z.lo(), -x, z);
+                final long et = fpow.pow(z.hi(), z.lo(), n - j, z);
+                // Aj = C(n, j) * T * S
+                //    = 2^e * [1, 2] * [0.5, 1] * [0.5, 1]
+                //    = 2^e * [0.25, 2]
+                e = ec + es + et;
+                // Only compute and add to the sum when the exponents overlap by n-bits.
+                if (e > esum - sumBits) {
+                    SDD.uncheckedMultiply(c.hi(), c.lo(), z.hi(), z.lo(), z);
+                    SDD.uncheckedMultiply(z.hi(), z.lo(), s, ss, z);
+                    // Scaling must offset by the scale of the sum
+                    SDD.ldexp(z.hi(), z.lo(), (int) (e - esum), z);
+                    fadd.add(sum.hi(), sum.lo(), z.hi(), z.lo(), sum);
+                } else {
+                    // Terms are expected to increase in magnitude then reduce.
+                    // Here the terms are insignificant and we can stop.
+                    // Effectively Aj -> eps * sum, and most of the computation is done.
+                    break;
+                }
+
+                // Re-scale the sum
+                esum += SDD.frexp(sum.hi(), sum.lo(), sum);
+            }
+
+            // p = x * sum(Ai). Since the sum is normalized
+            // this is safe as long as x does not approach a sub-normal.
+            // Typically x in (1/n, 1 - 1/n).
+            SDD.multiply(sum.hi(), sum.lo(), x, sum);
+            // Rescale the result
+            SDD.ldexp(sum.hi(), sum.lo(), (int) esum, sum);
+            if (sd) {
+                // SF = 1 - CDF
+                SDD.add(-sum.hi(), -sum.lo(), 1, sum);
+            }
+            return clipProbability(sum.doubleValue());
+        }
+
+        /**
+         * Calculates complementary probability {@code P[D_n^+ >= x]}, or survival
+         * function (SF), for the one-sided one-sample Kolmogorov-Smirnov distribution.
+         *
+         * <p>Computes the result using double-double arithmetic. The power function
+         * can use a fast approximation or a full power computation.
+         *
+         * <p>This function is safe for {@code x > 1/n}. When {@code x} approaches
+         * sub-normal then division or multiplication by x can under/overflow. The
+         * case of {@code x < 1/n} can be computed in {@code sfExact}.
+         *
+         * @param x Statistic (typically in (1/n, 1 - 1/n)).
+         * @param n Sample size (assumed to be positive).
+         * @return \(P(D_n^+ &ge; x)\)
+         * @see SDD#fastPowScaled(double, double, int, SDD)
+         * @see SDD#powScaled(double, double, int, SDD)
+         */
+        private static double sfDD(double x, int n) {
+            // Same initialisation as: double sf(double x, int n, ScaledPower power)
+
+            // Compute only the SF using Algorithm 1 pp 12.
+            // Only require 1 double-double for all intermediate computations.
+            final SDD zz = SDD.create();
+
+            // Compute: k = floor(n*x), alpha = nx - k; x = (k+alpha)/n with 0 <= alpha < 1
+            final int k = splitX(n, x, zz);
+            final double alpha = zz.hi();
+
+            // Choose the algorithm:
+            // Eq (13) Smirnov/Birnbaum-Tingey; or Smirnov/Dwass Eq (31)
+            // Eq. 13 sums j = 0 : floor( n(1-x) )  = n - 1 - floor(nx) iff alpha != 0; else n - floor(nx)
+            // Eq. 31 sums j = ceil( n(1-x) ) : n   = n - floor(nx)
+            // Drop a term term if x = (n-j)/n. Equates to shifting the floor* down and ceil* up:
+            // Eq. 13 N = floor*( n(1-x) ) = n - k - ((alpha!=0) ? 1 : 0) - ((alpha==0) ? 1 : 0)
+            // Eq. 31 N = n - ceil*( n(1-x) ) = k - ((alpha==0) ? 1 : 0)
+            // Where N is the number of terms - 1. This differs from Algorithm 1 by dropping
+            // a SD term when it should be zero (to working precision).
+            final int regN = n - k - 1;
+            final int sdN = k - ((alpha == 0) ? 1 : 0);
+
+            // SD : Figure 3 (c) (pp. 6)
+            // Terms Aj (j = n -> 0) have alternating signs through the range and may involve
+            // numbers much bigger than 1 causing cancellation; magnitudes increase then decrease.
+            // Section 3.3: Extra digits of precision required
+            // grows like Order(sqrt(n)). E.g. sf=0.7 (x ~ 0.4/sqrt(n)) loses 8 digits.
+            //
+            // Regular : Figure 3 (a, b)
+            // Terms Aj can have similar magnitude through the range; when x >= 1/sqrt(n)
+            // the final few terms can be magnitudes smaller and could be ignored.
+            // Section 3.4: As x increases the magnitude of terms becomes more peaked,
+            // centred at j = (n-nx)/2, i.e. 50% of the terms.
+            //
+            // As n -> inf the sf for x = k/n agrees with the asymptote Eq 5 in log2(n) bits.
+            //
+            // Figure 4 has lines at x = 1/n and x = 3/sqrt(n).
+            // Point between is approximately x = 4/n, i.e. nx < 4 : k <= 3.
+            // If faster when x < 0.5 and requiring nx ~ 4 then requires n >= 8.
+            //
+            // Note: If SD accuracy scales with sqrt(n) then we could use 1 / sqrt(n).
+            // That threshold is always above 4 / n when n is 16 (4/n = 1/sqrt(n) : n = 4^2).
+            // So the current thresholds are conservative.
+            boolean sd = false;
+            if (sdN < regN) {
+                // Here x < 0.5 and SD has fewer terms
+                // Always choose when we only have one additional term (i.e x < 2/n)
+                sd = sdN <= 1;
+                // Otherwise when x < 4 / n
+                sd |= sdN <= SD_MAX_TERMS && n >= SD_MIN_N;
+            }
+
+            final int maxN = sd ? sdN : regN;
+
+            // Note: if N > "very large" use the asymptotic approximation.
... 2010 lines suppressed ...