diff --git a/doap_math.rdf b/doap_math.rdf index a7282345b..ea3368f3f 100644 --- a/doap_math.rdf +++ b/doap_math.rdf @@ -41,66 +41,92 @@ 2015-04-17 3.5 + + commons-math 2015-01-11 3.4.1 + + commons-math 2014-12-26 3.4 + + commons-math 2014-05-14 3.3 + + commons-math 2013-04-06 3.2 + + commons-math 2013-01-13 3.1.1 + + commons-math 2012-12-23 3.1 + + commons-math 2012-03-08 3.0 + + commons-math 2011-03-02 2.2 + + commons-math 2010-04-02 2.1 + + commons-math 2009-08-03 2.0 + + commons-math 2008-02-24 1.2 + + commons-math 2005-12-19 1.1 + + commons-math 2004-12-06 diff --git a/pom.xml b/pom.xml index b88cf28d2..41c01ebc2 100644 --- a/pom.xml +++ b/pom.xml @@ -296,6 +296,9 @@ Sébastien Riou + + Benedikt Ritter + Bill Rossi diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d39acc0f2..6afa831f3 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -54,6 +54,19 @@ If the output is not quite correct, check for invisible trailing spaces! + + Use Double.isNaN rather than x != x in FastMath. + + + Fix potential branching errors in "FastMath#pow(double, double)" when + passing special values, i.e. infinity, due to erroneous JIT optimization. + + + Fixed equals/hashcode contract failure for Dfp. + + + Fixed wrong splitting of huge number in extended accuracy algorithms. + Added helper methods to FunctionUtils for univariate and multivariate differentiable functions conversion. diff --git a/src/main/java/org/apache/commons/math4/dfp/Dfp.java b/src/main/java/org/apache/commons/math4/dfp/Dfp.java index 7f15e8800..4c02ac0fa 100644 --- a/src/main/java/org/apache/commons/math4/dfp/Dfp.java +++ b/src/main/java/org/apache/commons/math4/dfp/Dfp.java @@ -924,7 +924,7 @@ public class Dfp implements RealFieldElement { */ @Override public int hashCode() { - return 17 + (sign << 8) + (nans << 16) + exp + Arrays.hashCode(mant); + return 17 + (isZero() ? 0 : (sign << 8)) + (nans << 16) + exp + Arrays.hashCode(mant); } /** Check if instance is not equal to x. diff --git a/src/main/java/org/apache/commons/math4/util/FastMath.java b/src/main/java/org/apache/commons/math4/util/FastMath.java index cc4caea6c..6e5bc1055 100644 --- a/src/main/java/org/apache/commons/math4/util/FastMath.java +++ b/src/main/java/org/apache/commons/math4/util/FastMath.java @@ -315,10 +315,17 @@ public class FastMath { /** Mask used to clear the non-sign part of a long. */ private static final long MASK_NON_SIGN_LONG = 0x7fffffffffffffffl; + /** Mask used to extract exponent from double bits. */ + private static final long MASK_DOUBLE_EXPONENT = 0x7ff0000000000000L; + + /** Mask used to extract mantissa from double bits. */ + private static final long MASK_DOUBLE_MANTISSA = 0x000fffffffffffffL; + + /** Mask used to add implicit high order bit for normalized double. */ + private static final long IMPLICIT_HIGH_BIT = 0x0010000000000000L; + /** 2^52 - double numbers this large must be integral (no fraction) or NaN or Infinite */ private static final double TWO_POWER_52 = 4503599627370496.0; - /** 2^53 - double numbers this large must be even. */ - private static final double TWO_POWER_53 = 2 * TWO_POWER_52; /** Constant: {@value}. */ private static final double F_1_3 = 1d / 3d; @@ -392,7 +399,7 @@ public class FastMath { * @return hyperbolic cosine of x */ public static double cosh(double x) { - if (x != x) { + if (Double.isNaN(x)) { return x; } @@ -462,7 +469,7 @@ public class FastMath { */ public static double sinh(double x) { boolean negate = false; - if (x != x) { + if (Double.isNaN(x)) { return x; } @@ -588,7 +595,7 @@ public class FastMath { public static double tanh(double x) { boolean negate = false; - if (x != x) { + if (Double.isNaN(x)) { return x; } @@ -991,7 +998,7 @@ public class FastMath { * @return exp(x) - 1 */ private static double expm1(double x, double hiPrecOut[]) { - if (x != x || x == 0.0) { // NaN or zero + if (Double.isNaN(x) || x == 0.0) { // NaN or zero return x; } @@ -1155,7 +1162,7 @@ public class FastMath { long bits = Double.doubleToRawLongBits(x); /* Handle special cases of negative input, and NaN */ - if (((bits & 0x8000000000000000L) != 0 || x != x) && x != 0.0) { + if (((bits & 0x8000000000000000L) != 0 || Double.isNaN(x)) && x != 0.0) { if (hiPrec != null) { hiPrec[0] = Double.NaN; } @@ -1458,141 +1465,141 @@ public class FastMath { * @return double */ public static double pow(final double x, final double y) { - final double lns[] = new double[2]; - if (y == 0.0) { + if (y == 0) { + // y = -0 or y = +0 return 1.0; - } else if (x != x) { // X is NaN - return x; - } else if (y != y) { // y is NaN - return y; - } else if (x == 0) { - long bits = Double.doubleToRawLongBits(x); - if ((bits & 0x8000000000000000L) != 0) { - // -zero - long yi = (long) y; - - if (y < 0 && y == yi && (yi & 1) == 1) { - return Double.NEGATIVE_INFINITY; - } - - if (y > 0 && y == yi && (yi & 1) == 1) { - return -0.0; - } - } - - if (y < 0) { - return Double.POSITIVE_INFINITY; - } else { - return 0.0; - } - } else if (x == Double.POSITIVE_INFINITY) { - if (y < 0.0) { - return 0.0; - } else { - return Double.POSITIVE_INFINITY; - } - } else if (y == Double.POSITIVE_INFINITY) { - if (x * x == 1.0) { - return Double.NaN; - } - - if (x * x > 1.0) { - return Double.POSITIVE_INFINITY; - } else { - return 0.0; - } - } else if (x == Double.NEGATIVE_INFINITY) { - if (y < 0) { - long yi = (long) y; - if (y == yi && (yi & 1) == 1) { - return -0.0; - } - - return 0.0; - } - - if (y > 0) { - long yi = (long) y; - if (y == yi && (yi & 1) == 1) { - return Double.NEGATIVE_INFINITY; - } - - return Double.POSITIVE_INFINITY; - } - } else if (y == Double.NEGATIVE_INFINITY) { - if (x * x == 1.0) { - return Double.NaN; - } - - if (x * x < 1.0) { - return Double.POSITIVE_INFINITY; - } else { - return 0.0; - } - } else if (x < 0) { /* Handle special case x<0 */ - // y is an even integer in this case - if (y >= TWO_POWER_53 || y <= -TWO_POWER_53) { - return pow(-x, y); - } - - if (y == (long) y) { - // If y is an integer - return ((long)y & 1) == 0 ? pow(-x, y) : -pow(-x, y); - } else { - return Double.NaN; - } - } - - /* Split y into ya and yb such that y = ya+yb */ - double ya; - double yb; - if (y < 8e298 && y > -8e298) { - double tmp1 = y * HEX_40000000; - ya = y + tmp1 - tmp1; - yb = y - ya; } else { - double tmp1 = y * 9.31322574615478515625E-10; - double tmp2 = tmp1 * 9.31322574615478515625E-10; - ya = (tmp1 + tmp2 - tmp1) * HEX_40000000 * HEX_40000000; - yb = y - ya; + + final long yBits = Double.doubleToRawLongBits(y); + final int yRawExp = (int) ((yBits & MASK_DOUBLE_EXPONENT) >> 52); + final long yRawMantissa = yBits & MASK_DOUBLE_MANTISSA; + final long xBits = Double.doubleToRawLongBits(x); + final int xRawExp = (int) ((xBits & MASK_DOUBLE_EXPONENT) >> 52); + final long xRawMantissa = xBits & MASK_DOUBLE_MANTISSA; + + if (yRawExp == 2047) { + // special values for y + if (yRawMantissa == 0) { + // y = -infinity or y = +infinity + if ((xRawExp == 2047 && xRawMantissa != 0) || (xRawExp == 1023 && xRawMantissa == 0)) { + // x = NaN or x = -1.0 or x = +1.0 + return Double.NaN; + } else { + // on both sides of the limit points abs(x) = 1, + // infinite power asymtotically reaches different limits + if ((y > 0) ^ (xRawExp < 1023)) { + // either y = +infinity and abs(x) > 1.0 + // or y = -infinity and abs(x) < 1.0 + return Double.POSITIVE_INFINITY; + } else { + // either y = +infinity and abs(x) < 1.0 + // or y = -infinity and abs(x) > 1.0 + return +0.0; + } + } + } else { + // NaN + return Double.NaN; + } + } else { + // y is a regular non-zero number + + if (yRawExp >= 1023) { + // y may be an integral value, which should be handled specifically + final long yFullMantissa = IMPLICIT_HIGH_BIT | yRawMantissa; + if (yRawExp < 1075) { + // normal number with negative shift that may have a fractional part + final long integralMask = (-1L) << (1075 - yRawExp); + if ((yFullMantissa & integralMask) == yFullMantissa) { + // all fractional bits are 0, the number is really integral + final long l = yFullMantissa >> (1075 - yRawExp); + return FastMath.pow(x, (y < 0) ? -l : l); + } + } else { + // normal number with positive shift, always an integral value + double shiftedX = (y < 0) ? 1.0 / x : x; + int shiftedExp = yRawExp; + if (shiftedExp > 1085) { + // the integral value is too large to fit in a primitive long + // we want an intermediate odd power (in order not to lose the sign) + // and such that the remaining operation will not have power 0 (so result is not forced to 1.0) + final int intermediatePower = ((shiftedExp & 0x1) == 0) ? shiftedExp - 1077 : shiftedExp - 1076; + shiftedX = FastMath.pow(shiftedX, intermediatePower); + shiftedExp -= intermediatePower; + } + // the integral value fits in a primitive long + return FastMath.pow(shiftedX, yFullMantissa << (shiftedExp - 1075)); + } + } + + // y is a non-integral value + + if (x == 0) { + // x = -0 or x = +0 + // the integer powers have already been handled above + return y < 0 ? Double.POSITIVE_INFINITY : +0.0; + } else if (xRawExp == 2047) { + if (xRawMantissa == 0) { + // x = -infinity or x = +infinity + return (y < 0) ? +0.0 : Double.POSITIVE_INFINITY; + } else { + // NaN + return Double.NaN; + } + } else if (x < 0) { + // the integer powers have already been handled above + return Double.NaN; + } else { + + // this is the general case, for regular fractional numbers x and y + + // Split y into ya and yb such that y = ya+yb + final double tmp = y * HEX_40000000; + final double ya = (y + tmp) - tmp; + final double yb = y - ya; + + /* Compute ln(x) */ + final double lns[] = new double[2]; + final double lores = log(x, lns); + if (Double.isInfinite(lores)) { // don't allow this to be converted to NaN + return lores; + } + + double lna = lns[0]; + double lnb = lns[1]; + + /* resplit lns */ + final double tmp1 = lna * HEX_40000000; + final double tmp2 = (lna + tmp1) - tmp1; + lnb += lna - tmp2; + lna = tmp2; + + // y*ln(x) = (aa+ab) + final double aa = lna * ya; + final double ab = lna * yb + lnb * ya + lnb * yb; + + lna = aa+ab; + lnb = -(lna - aa - ab); + + double z = 1.0 / 120.0; + z = z * lnb + (1.0 / 24.0); + z = z * lnb + (1.0 / 6.0); + z = z * lnb + 0.5; + z = z * lnb + 1.0; + z *= lnb; + + final double result = exp(lna, z, null); + //result = result + result * z; + return result; + + } + } + } - /* Compute ln(x) */ - final double lores = log(x, lns); - if (Double.isInfinite(lores)){ // don't allow this to be converted to NaN - return lores; - } - - double lna = lns[0]; - double lnb = lns[1]; - - /* resplit lns */ - double tmp1 = lna * HEX_40000000; - double tmp2 = lna + tmp1 - tmp1; - lnb += lna - tmp2; - lna = tmp2; - - // y*ln(x) = (aa+ab) - final double aa = lna * ya; - final double ab = lna * yb + lnb * ya + lnb * yb; - - lna = aa+ab; - lnb = -(lna - aa - ab); - - double z = 1.0 / 120.0; - z = z * lnb + (1.0 / 24.0); - z = z * lnb + (1.0 / 6.0); - z = z * lnb + 0.5; - z = z * lnb + 1.0; - z *= lnb; - - final double result = exp(lna, z, null); - //result = result + result * z; - return result; } - /** * Raise a double to an int power. * @@ -1602,62 +1609,152 @@ public class FastMath { * @since 3.1 */ public static double pow(double d, int e) { + return pow(d, (long) e); + } + + /** + * Raise a double to a long power. + * + * @param d Number to raise. + * @param e Exponent. + * @return de + * @since 4.0 + */ + public static double pow(double d, long e) { if (e == 0) { return 1.0; - } else if (e < 0) { - e = -e; - d = 1.0 / d; + } else if (e > 0) { + return new Split(d).pow(e).full; + } else { + return new Split(d).reciprocal().pow(-e).full; + } + } + + /** Class operator on double numbers split into one 26 bits number and one 27 bits number. */ + private static class Split { + + /** Split version of NaN. */ + public static final Split NAN = new Split(Double.NaN, 0); + + /** Split version of positive infinity. */ + public static final Split POSITIVE_INFINITY = new Split(Double.POSITIVE_INFINITY, 0); + + /** Split version of negative infinity. */ + public static final Split NEGATIVE_INFINITY = new Split(Double.NEGATIVE_INFINITY, 0); + + /** Full number. */ + private final double full; + + /** High order bits. */ + private final double high; + + /** Low order bits. */ + private final double low; + + /** Simple constructor. + * @param x number to split + */ + public Split(final double x) { + full = x; + high = Double.longBitsToDouble(Double.doubleToRawLongBits(x) & ((-1L) << 27)); + low = x - high; } - // split d as two 26 bits numbers - // beware the following expressions must NOT be simplified, they rely on floating point arithmetic properties - final int splitFactor = 0x8000001; - final double cd = splitFactor * d; - final double d1High = cd - (cd - d); - final double d1Low = d - d1High; + /** Simple constructor. + * @param high high order bits + * @param low low order bits + */ + public Split(final double high, final double low) { + this(high + low, high, low); + } - // prepare result - double resultHigh = 1; - double resultLow = 0; + /** Simple constructor. + * @param full full number + * @param high high order bits + * @param low low order bits + */ + public Split(final double full, final double high, final double low) { + this.full = full; + this.high = high; + this.low = low; + } - // d^(2p) - double d2p = d; - double d2pHigh = d1High; - double d2pLow = d1Low; + /** Multiply the instance by another one. + * @param b other instance to multiply by + * @return product + */ + public Split multiply(final Split b) { + // beware the following expressions must NOT be simplified, they rely on floating point arithmetic properties + final Split mulBasic = new Split(full * b.full); + final double mulError = low * b.low - (((mulBasic.full - high * b.high) - low * b.high) - high * b.low); + return new Split(mulBasic.high, mulBasic.low + mulError); + } - while (e != 0) { + /** Compute the reciprocal of the instance. + * @return reciprocal of the instance + */ + public Split reciprocal() { + + final double approximateInv = 1.0 / full; + final Split splitInv = new Split(approximateInv); + + // if 1.0/d were computed perfectly, remultiplying it by d should give 1.0 + // we want to estimate the error so we can fix the low order bits of approximateInvLow + // beware the following expressions must NOT be simplified, they rely on floating point arithmetic properties + final Split product = multiply(splitInv); + final double error = (product.high - 1) + product.low; + + // better accuracy estimate of reciprocal + return Double.isNaN(error) ? splitInv : new Split(splitInv.high, splitInv.low - error / full); + + } + + /** Computes this^e. + * @param e exponent (beware, here it MUST be > 0) + * @return d^e, split in high and low bits + * @since 4.0 + */ + private Split pow(final long e) { + + // prepare result + Split result = new Split(1); + + // d^(2p) + Split d2p = new Split(full, high, low); + + for (long p = e; p != 0; p >>= 1) { + + if ((p & 0x1) != 0) { + // accurate multiplication result = result * d^(2p) using Veltkamp TwoProduct algorithm + result = result.multiply(d2p); + } + + // accurate squaring d^(2(p+1)) = d^(2p) * d^(2p) using Veltkamp TwoProduct algorithm + d2p = d2p.multiply(d2p); - if ((e & 0x1) != 0) { - // accurate multiplication result = result * d^(2p) using Veltkamp TwoProduct algorithm - // beware the following expressions must NOT be simplified, they rely on floating point arithmetic properties - final double tmpHigh = resultHigh * d2p; - final double cRH = splitFactor * resultHigh; - final double rHH = cRH - (cRH - resultHigh); - final double rHL = resultHigh - rHH; - final double tmpLow = rHL * d2pLow - (((tmpHigh - rHH * d2pHigh) - rHL * d2pHigh) - rHH * d2pLow); - resultHigh = tmpHigh; - resultLow = resultLow * d2p + tmpLow; } - // accurate squaring d^(2(p+1)) = d^(2p) * d^(2p) using Veltkamp TwoProduct algorithm - // beware the following expressions must NOT be simplified, they rely on floating point arithmetic properties - final double tmpHigh = d2pHigh * d2p; - final double cD2pH = splitFactor * d2pHigh; - final double d2pHH = cD2pH - (cD2pH - d2pHigh); - final double d2pHL = d2pHigh - d2pHH; - final double tmpLow = d2pHL * d2pLow - (((tmpHigh - d2pHH * d2pHigh) - d2pHL * d2pHigh) - d2pHH * d2pLow); - final double cTmpH = splitFactor * tmpHigh; - d2pHigh = cTmpH - (cTmpH - tmpHigh); - d2pLow = d2pLow * d2p + tmpLow + (tmpHigh - d2pHigh); - d2p = d2pHigh + d2pLow; - - e >>= 1; + if (Double.isNaN(result.full)) { + if (Double.isNaN(full)) { + return Split.NAN; + } else { + // some intermediate numbers exceeded capacity, + // and the low order bits became NaN (because infinity - infinity = NaN) + if (FastMath.abs(full) < 1) { + return new Split(FastMath.copySign(0.0, full), 0.0); + } else if (full < 0 && (e & 0x1) == 1) { + return Split.NEGATIVE_INFINITY; + } else { + return Split.POSITIVE_INFINITY; + } + } + } else { + return result; + } } - return resultHigh + resultLow; - } /** @@ -2576,7 +2673,7 @@ public class FastMath { * @return phase angle of point (x,y) between {@code -PI} and {@code PI} */ public static double atan2(double y, double x) { - if (x != x || y != y) { + if (Double.isNaN(x) || Double.isNaN(y)) { return Double.NaN; } @@ -2697,7 +2794,7 @@ public class FastMath { * @return arc sine of x */ public static double asin(double x) { - if (x != x) { + if (Double.isNaN(x)) { return Double.NaN; } @@ -2773,7 +2870,7 @@ public class FastMath { * @return arc cosine of x */ public static double acos(double x) { - if (x != x) { + if (Double.isNaN(x)) { return Double.NaN; } @@ -3333,7 +3430,7 @@ public class FastMath { public static double floor(double x) { long y; - if (x != x) { // NaN + if (Double.isNaN(x)) { return x; } @@ -3360,7 +3457,7 @@ public class FastMath { public static double ceil(double x) { double y; - if (x != x) { // NaN + if (Double.isNaN(x)) { return x; } diff --git a/src/main/java/org/apache/commons/math4/util/MathArrays.java b/src/main/java/org/apache/commons/math4/util/MathArrays.java index ea2e9f248..f4e7f4e4e 100644 --- a/src/main/java/org/apache/commons/math4/util/MathArrays.java +++ b/src/main/java/org/apache/commons/math4/util/MathArrays.java @@ -47,8 +47,6 @@ import org.apache.commons.math4.random.Well19937c; * @since 3.0 */ public class MathArrays { - /** Factor used for splitting double numbers: n = 2^27 + 1 (i.e. {@value}). */ - private static final int SPLIT_FACTOR = 0x8000001; /** * Private constructor. @@ -864,15 +862,13 @@ public class MathArrays { double prodLowSum = 0; for (int i = 0; i < len; i++) { - final double ai = a[i]; - final double ca = SPLIT_FACTOR * ai; - final double aHigh = ca - (ca - ai); - final double aLow = ai - aHigh; + final double ai = a[i]; + final double aHigh = Double.longBitsToDouble(Double.doubleToRawLongBits(ai) & ((-1L) << 27)); + final double aLow = ai - aHigh; - final double bi = b[i]; - final double cb = SPLIT_FACTOR * bi; - final double bHigh = cb - (cb - bi); - final double bLow = bi - bHigh; + final double bi = b[i]; + final double bHigh = Double.longBitsToDouble(Double.doubleToRawLongBits(bi) & ((-1L) << 27)); + final double bLow = bi - bHigh; prodHigh[i] = ai * bi; final double prodLow = aLow * bLow - (((prodHigh[i] - aHigh * bHigh) - @@ -938,7 +934,6 @@ public class MathArrays { // the code below is split in many additions/subtractions that may // appear redundant. However, they should NOT be simplified, as they // use IEEE754 floating point arithmetic rounding properties. - // as an example, the expression "ca1 - (ca1 - a1)" is NOT the same as "a1" // The variable naming conventions are that xyzHigh contains the most significant // bits of xyz and xyzLow contains its least significant bits. So theoretically // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot @@ -946,24 +941,20 @@ public class MathArrays { // to hold it as long as we can, combining the high and low order bits together // only at the end, after cancellation may have occurred on high order bits - // split a1 and b1 as two 26 bits numbers - final double ca1 = SPLIT_FACTOR * a1; - final double a1High = ca1 - (ca1 - a1); + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); final double a1Low = a1 - a1High; - final double cb1 = SPLIT_FACTOR * b1; - final double b1High = cb1 - (cb1 - b1); + final double b1High = Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); final double b1Low = b1 - b1High; // accurate multiplication a1 * b1 final double prod1High = a1 * b1; final double prod1Low = a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); - // split a2 and b2 as two 26 bits numbers - final double ca2 = SPLIT_FACTOR * a2; - final double a2High = ca2 - (ca2 - a2); + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); final double a2Low = a2 - a2High; - final double cb2 = SPLIT_FACTOR * b2; - final double b2High = cb2 - (cb2 - b2); + final double b2High = Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); final double b2Low = b2 - b2High; // accurate multiplication a2 * b2 @@ -1018,7 +1009,6 @@ public class MathArrays { // the code below is split in many additions/subtractions that may // appear redundant. However, they should NOT be simplified, as they // do use IEEE754 floating point arithmetic rounding properties. - // as an example, the expression "ca1 - (ca1 - a1)" is NOT the same as "a1" // The variables naming conventions are that xyzHigh contains the most significant // bits of xyz and xyzLow contains its least significant bits. So theoretically // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot @@ -1026,36 +1016,30 @@ public class MathArrays { // to hold it as long as we can, combining the high and low order bits together // only at the end, after cancellation may have occurred on high order bits - // split a1 and b1 as two 26 bits numbers - final double ca1 = SPLIT_FACTOR * a1; - final double a1High = ca1 - (ca1 - a1); + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); final double a1Low = a1 - a1High; - final double cb1 = SPLIT_FACTOR * b1; - final double b1High = cb1 - (cb1 - b1); + final double b1High = Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); final double b1Low = b1 - b1High; // accurate multiplication a1 * b1 final double prod1High = a1 * b1; final double prod1Low = a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); - // split a2 and b2 as two 26 bits numbers - final double ca2 = SPLIT_FACTOR * a2; - final double a2High = ca2 - (ca2 - a2); + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); final double a2Low = a2 - a2High; - final double cb2 = SPLIT_FACTOR * b2; - final double b2High = cb2 - (cb2 - b2); + final double b2High = Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); final double b2Low = b2 - b2High; // accurate multiplication a2 * b2 final double prod2High = a2 * b2; final double prod2Low = a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low); - // split a3 and b3 as two 26 bits numbers - final double ca3 = SPLIT_FACTOR * a3; - final double a3High = ca3 - (ca3 - a3); + // split a3 and b3 as one 26 bits number and one 27 bits number + final double a3High = Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27)); final double a3Low = a3 - a3High; - final double cb3 = SPLIT_FACTOR * b3; - final double b3High = cb3 - (cb3 - b3); + final double b3High = Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27)); final double b3Low = b3 - b3High; // accurate multiplication a3 * b3 @@ -1120,7 +1104,6 @@ public class MathArrays { // the code below is split in many additions/subtractions that may // appear redundant. However, they should NOT be simplified, as they // do use IEEE754 floating point arithmetic rounding properties. - // as an example, the expression "ca1 - (ca1 - a1)" is NOT the same as "a1" // The variables naming conventions are that xyzHigh contains the most significant // bits of xyz and xyzLow contains its least significant bits. So theoretically // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot @@ -1128,48 +1111,40 @@ public class MathArrays { // to hold it as long as we can, combining the high and low order bits together // only at the end, after cancellation may have occurred on high order bits - // split a1 and b1 as two 26 bits numbers - final double ca1 = SPLIT_FACTOR * a1; - final double a1High = ca1 - (ca1 - a1); + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); final double a1Low = a1 - a1High; - final double cb1 = SPLIT_FACTOR * b1; - final double b1High = cb1 - (cb1 - b1); + final double b1High = Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); final double b1Low = b1 - b1High; // accurate multiplication a1 * b1 final double prod1High = a1 * b1; final double prod1Low = a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); - // split a2 and b2 as two 26 bits numbers - final double ca2 = SPLIT_FACTOR * a2; - final double a2High = ca2 - (ca2 - a2); + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); final double a2Low = a2 - a2High; - final double cb2 = SPLIT_FACTOR * b2; - final double b2High = cb2 - (cb2 - b2); + final double b2High = Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); final double b2Low = b2 - b2High; // accurate multiplication a2 * b2 final double prod2High = a2 * b2; final double prod2Low = a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low); - // split a3 and b3 as two 26 bits numbers - final double ca3 = SPLIT_FACTOR * a3; - final double a3High = ca3 - (ca3 - a3); + // split a3 and b3 as one 26 bits number and one 27 bits number + final double a3High = Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27)); final double a3Low = a3 - a3High; - final double cb3 = SPLIT_FACTOR * b3; - final double b3High = cb3 - (cb3 - b3); + final double b3High = Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27)); final double b3Low = b3 - b3High; // accurate multiplication a3 * b3 final double prod3High = a3 * b3; final double prod3Low = a3Low * b3Low - (((prod3High - a3High * b3High) - a3Low * b3High) - a3High * b3Low); - // split a4 and b4 as two 26 bits numbers - final double ca4 = SPLIT_FACTOR * a4; - final double a4High = ca4 - (ca4 - a4); + // split a4 and b4 as one 26 bits number and one 27 bits number + final double a4High = Double.longBitsToDouble(Double.doubleToRawLongBits(a4) & ((-1L) << 27)); final double a4Low = a4 - a4High; - final double cb4 = SPLIT_FACTOR * b4; - final double b4High = cb4 - (cb4 - b4); + final double b4High = Double.longBitsToDouble(Double.doubleToRawLongBits(b4) & ((-1L) << 27)); final double b4Low = b4 - b4High; // accurate multiplication a4 * b4 diff --git a/src/test/java/org/apache/commons/math4/dfp/DfpTest.java b/src/test/java/org/apache/commons/math4/dfp/DfpTest.java index 9725f3644..c9b375ad1 100644 --- a/src/test/java/org/apache/commons/math4/dfp/DfpTest.java +++ b/src/test/java/org/apache/commons/math4/dfp/DfpTest.java @@ -1652,4 +1652,14 @@ public class DfpTest extends ExtendedFieldElementAbstractTest { Assert.assertTrue(field.newDfp("NaN").isNaN()); } + @Test + public void testEqualsHashcodeContract() { + DfpField var1 = new DfpField(1); + Dfp var6 = var1.newDfp(-0.0d); + Dfp var5 = var1.newDfp(0L); + + // Checks the contract: equals-hashcode on var5 and var6 + Assert.assertTrue(var5.equals(var6) ? var5.hashCode() == var6.hashCode() : true); + } + } diff --git a/src/test/java/org/apache/commons/math4/distribution/GammaDistributionTest.java b/src/test/java/org/apache/commons/math4/distribution/GammaDistributionTest.java index 0316c0ffa..26c0a1c93 100644 --- a/src/test/java/org/apache/commons/math4/distribution/GammaDistributionTest.java +++ b/src/test/java/org/apache/commons/math4/distribution/GammaDistributionTest.java @@ -350,7 +350,7 @@ public class GammaDistributionTest extends RealDistributionAbstractTest { @Test public void testMath753Shape142() throws IOException { - doTestMath753(142.0, 0.5, 1.5, 40.0, 40.0, "gamma-distribution-shape-142.csv"); + doTestMath753(142.0, 3.3, 1.6, 40.0, 40.0, "gamma-distribution-shape-142.csv"); } @Test diff --git a/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/FieldVector3DTest.java b/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/FieldVector3DTest.java index 48e41e293..d59850fa2 100644 --- a/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/FieldVector3DTest.java +++ b/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/FieldVector3DTest.java @@ -585,7 +585,7 @@ public class FieldVector3DTest { DerivativeStructure sNaive = u1.getX().multiply(u2.getX()).add(u1.getY().multiply(u2.getY())).add(u1.getZ().multiply(u2.getZ())); DerivativeStructure sAccurate = FieldVector3D.dotProduct(u1, u2); Assert.assertEquals(0.0, sNaive.getReal(), 1.0e-30); - Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate.getReal(), 1.0e-16); + Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate.getReal(), 1.0e-15); } @Test diff --git a/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/Vector3DTest.java b/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/Vector3DTest.java index c3bdf9fb4..d53a0f50c 100644 --- a/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/Vector3DTest.java +++ b/src/test/java/org/apache/commons/math4/geometry/euclidean/threed/Vector3DTest.java @@ -343,7 +343,7 @@ public class Vector3DTest { double sNaive = u1.getX() * u2.getX() + u1.getY() * u2.getY() + u1.getZ() * u2.getZ(); double sAccurate = u1.dotProduct(u2); Assert.assertEquals(0.0, sNaive, 1.0e-30); - Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate, 1.0e-16); + Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate, 1.0e-15); } @Test diff --git a/src/test/java/org/apache/commons/math4/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java b/src/test/java/org/apache/commons/math4/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java index cd02d9d00..833785d64 100644 --- a/src/test/java/org/apache/commons/math4/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java +++ b/src/test/java/org/apache/commons/math4/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java @@ -189,7 +189,7 @@ public class BOBYQAOptimizerTest { new PointValuePair(point(DIM/2,0.0),0.0); doTest(new DiffPow(), startPoint, boundaries, GoalType.MINIMIZE, - 1e-8, 1e-1, 12000, expected); + 1e-8, 1e-1, 21000, expected); } @Test diff --git a/src/test/java/org/apache/commons/math4/util/FastMathTest.java b/src/test/java/org/apache/commons/math4/util/FastMathTest.java index 529bba4c5..3c033ee4c 100644 --- a/src/test/java/org/apache/commons/math4/util/FastMathTest.java +++ b/src/test/java/org/apache/commons/math4/util/FastMathTest.java @@ -338,9 +338,9 @@ public class FastMathTest { @Test public void testLogSpecialCases() { - Assert.assertTrue("Log of zero should be -Inf", Double.isInfinite(FastMath.log(0.0))); + Assert.assertEquals("Log of zero should be -Inf", Double.NEGATIVE_INFINITY, FastMath.log(0.0), 1.0); - Assert.assertTrue("Log of -zero should be -Inf", Double.isInfinite(FastMath.log(-0.0))); + Assert.assertEquals("Log of -zero should be -Inf", Double.NEGATIVE_INFINITY, FastMath.log(-0.0), 1.0); Assert.assertTrue("Log of NaN should be NaN", Double.isNaN(FastMath.log(Double.NaN))); @@ -348,8 +348,9 @@ public class FastMathTest { Assert.assertEquals("Log of Double.MIN_VALUE should be -744.4400719213812", -744.4400719213812, FastMath.log(Double.MIN_VALUE), Precision.EPSILON); - Assert.assertTrue("Log of infinity should be infinity", Double.isInfinite(FastMath.log(Double.POSITIVE_INFINITY))); + Assert.assertEquals("Log of infinity should be infinity", Double.POSITIVE_INFINITY, FastMath.log(Double.POSITIVE_INFINITY), 1.0); } + @Test public void testExpSpecialCases() { @@ -360,7 +361,7 @@ public class FastMathTest { Assert.assertTrue("exp of NaN should be NaN", Double.isNaN(FastMath.exp(Double.NaN))); - Assert.assertTrue("exp of infinity should be infinity", Double.isInfinite(FastMath.exp(Double.POSITIVE_INFINITY))); + Assert.assertEquals("exp of infinity should be infinity", Double.POSITIVE_INFINITY, FastMath.exp(Double.POSITIVE_INFINITY), 1.0); Assert.assertEquals("exp of -infinity should be 0.0", 0.0, FastMath.exp(Double.NEGATIVE_INFINITY), Precision.EPSILON); @@ -383,9 +384,11 @@ public class FastMathTest { Assert.assertTrue("pow(NaN, PI) should be NaN", Double.isNaN(FastMath.pow(Double.NaN, Math.PI))); - Assert.assertTrue("pow(2.0, Infinity) should be Infinity", Double.isInfinite(FastMath.pow(2.0, Double.POSITIVE_INFINITY))); + Assert.assertEquals("pow(2.0, Infinity) should be Infinity", Double.POSITIVE_INFINITY, FastMath.pow(2.0, Double.POSITIVE_INFINITY), 1.0); - Assert.assertTrue("pow(0.5, -Infinity) should be Infinity", Double.isInfinite(FastMath.pow(0.5, Double.NEGATIVE_INFINITY))); + Assert.assertEquals("pow(0.5, -Infinity) should be Infinity", Double.POSITIVE_INFINITY, FastMath.pow(0.5, Double.NEGATIVE_INFINITY), 1.0); + + Assert.assertEquals("pow(0.5, Infinity) should be 0.0", 0.0, FastMath.pow(0.5, Double.POSITIVE_INFINITY), Precision.EPSILON); Assert.assertEquals("pow(2.0, -Infinity) should be 0.0", 0.0, FastMath.pow(2.0, Double.NEGATIVE_INFINITY), Precision.EPSILON); @@ -393,22 +396,50 @@ public class FastMathTest { Assert.assertEquals("pow(Infinity, -0.5) should be 0.0", 0.0, FastMath.pow(Double.POSITIVE_INFINITY, -0.5), Precision.EPSILON); - Assert.assertTrue("pow(0.0, -0.5) should be Inf", Double.isInfinite(FastMath.pow(0.0, -0.5))); + Assert.assertEquals("pow(0.0, -0.5) should be Inf", Double.POSITIVE_INFINITY, FastMath.pow(0.0, -0.5), 1.0); - Assert.assertTrue("pow(Inf, 0.5) should be Inf", Double.isInfinite(FastMath.pow(Double.POSITIVE_INFINITY, 0.5))); + Assert.assertEquals("pow(Inf, 0.5) should be Inf", Double.POSITIVE_INFINITY, FastMath.pow(Double.POSITIVE_INFINITY, 0.5), 1.0); - Assert.assertTrue("pow(-0.0, -3.0) should be -Inf", Double.isInfinite(FastMath.pow(-0.0, -3.0))); + Assert.assertEquals("pow(-0.0, -3.0) should be -Inf", Double.NEGATIVE_INFINITY, FastMath.pow(-0.0, -3.0), 1.0); - Assert.assertTrue("pow(-Inf, -3.0) should be -Inf", Double.isInfinite(FastMath.pow(Double.NEGATIVE_INFINITY, 3.0))); + Assert.assertEquals("pow(-0.0, Infinity) should be 0.0", 0.0, FastMath.pow(-0.0, Double.POSITIVE_INFINITY), Precision.EPSILON); - Assert.assertTrue("pow(-0.0, -3.5) should be Inf", Double.isInfinite(FastMath.pow(-0.0, -3.5))); + Assert.assertTrue("pow(-0.0, NaN) should be NaN", Double.isNaN(FastMath.pow(-0.0, Double.NaN))); - Assert.assertTrue("pow(Inf, 3.5) should be Inf", Double.isInfinite(FastMath.pow(Double.POSITIVE_INFINITY, 3.5))); + Assert.assertEquals("pow(-0.0, -tiny) should be Infinity", Double.POSITIVE_INFINITY, FastMath.pow(-0.0, -Double.MIN_VALUE), 1.0); + + Assert.assertEquals("pow(-0.0, -huge) should be Infinity", Double.POSITIVE_INFINITY, FastMath.pow(-0.0, -Double.MAX_VALUE), 1.0); + + Assert.assertEquals("pow(-Inf, -3.0) should be -Inf", Double.NEGATIVE_INFINITY, FastMath.pow(Double.NEGATIVE_INFINITY, 3.0), 1.0); + + Assert.assertEquals("pow(-0.0, -3.5) should be Inf", Double.POSITIVE_INFINITY, FastMath.pow(-0.0, -3.5), 1.0); + + Assert.assertEquals("pow(Inf, 3.5) should be Inf", Double.POSITIVE_INFINITY, FastMath.pow(Double.POSITIVE_INFINITY, 3.5), 1.0); Assert.assertEquals("pow(-2.0, 3.0) should be -8.0", -8.0, FastMath.pow(-2.0, 3.0), Precision.EPSILON); Assert.assertTrue("pow(-2.0, 3.5) should be NaN", Double.isNaN(FastMath.pow(-2.0, 3.5))); + Assert.assertTrue("pow(NaN, -Infinity) should be NaN", Double.isNaN(FastMath.pow(Double.NaN, Double.NEGATIVE_INFINITY))); + + Assert.assertEquals("pow(NaN, 0.0) should be 1.0", 1.0, FastMath.pow(Double.NaN, 0.0), Precision.EPSILON); + + Assert.assertEquals("pow(-Infinity, -Infinity) should be 0.0", 0.0, FastMath.pow(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), Precision.EPSILON); + + Assert.assertEquals("pow(-huge, -huge) should be 0.0", 0.0, FastMath.pow(-Double.MAX_VALUE, -Double.MAX_VALUE), Precision.EPSILON); + + Assert.assertTrue("pow(-huge, huge) should be +Inf", Double.isInfinite(FastMath.pow(-Double.MAX_VALUE, Double.MAX_VALUE))); + + Assert.assertTrue("pow(NaN, -Infinity) should be NaN", Double.isNaN(FastMath.pow(Double.NaN, Double.NEGATIVE_INFINITY))); + + Assert.assertEquals("pow(NaN, 0.0) should be 1.0", 1.0, FastMath.pow(Double.NaN, 0.0), Precision.EPSILON); + + Assert.assertEquals("pow(-Infinity, -Infinity) should be 0.0", 0.0, FastMath.pow(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), Precision.EPSILON); + + Assert.assertEquals("pow(-huge, -huge) should be 0.0", 0.0, FastMath.pow(-Double.MAX_VALUE, -Double.MAX_VALUE), Precision.EPSILON); + + Assert.assertEquals("pow(-huge, huge) should be +Inf", Double.POSITIVE_INFINITY, FastMath.pow(-Double.MAX_VALUE, Double.MAX_VALUE), 1.0); + // Added tests for a 100% coverage Assert.assertTrue("pow(+Inf, NaN) should be NaN", Double.isNaN(FastMath.pow(Double.POSITIVE_INFINITY, Double.NaN))); @@ -421,9 +452,9 @@ public class FastMathTest { Assert.assertEquals("pow(-Inf, -2.0) should be 0.0", 0.0, FastMath.pow(Double.NEGATIVE_INFINITY, -2.0), Precision.EPSILON); - Assert.assertTrue("pow(-Inf, 1.0) should be -Inf", Double.isInfinite(FastMath.pow(Double.NEGATIVE_INFINITY, 1.0))); + Assert.assertEquals("pow(-Inf, 1.0) should be -Inf", Double.NEGATIVE_INFINITY, FastMath.pow(Double.NEGATIVE_INFINITY, 1.0), 1.0); - Assert.assertTrue("pow(-Inf, 2.0) should be +Inf", Double.isInfinite(FastMath.pow(Double.NEGATIVE_INFINITY, 2.0))); + Assert.assertEquals("pow(-Inf, 2.0) should be +Inf", Double.POSITIVE_INFINITY, FastMath.pow(Double.NEGATIVE_INFINITY, 2.0), 1.0); Assert.assertTrue("pow(1.0, -Inf) should be NaN", Double.isNaN(FastMath.pow(1.0, Double.NEGATIVE_INFINITY))); @@ -1203,6 +1234,11 @@ public class FastMathTest { } } + @Test + public void testIntPowHuge() { + Assert.assertTrue(Double.isInfinite(FastMath.pow(FastMath.scalb(1.0, 500), 4))); + } + @Test public void testIncrementExactInt() { int[] specialValues = new int[] { diff --git a/src/test/java/org/apache/commons/math4/util/MathArraysTest.java b/src/test/java/org/apache/commons/math4/util/MathArraysTest.java index 16e6a529f..3abd0cd13 100644 --- a/src/test/java/org/apache/commons/math4/util/MathArraysTest.java +++ b/src/test/java/org/apache/commons/math4/util/MathArraysTest.java @@ -711,6 +711,39 @@ public class MathArraysTest { } } + @Test + public void testLinearCombinationHuge() { + int scale = 971; + final double[] a = new double[] { + -1321008684645961.0 / 268435456.0, + -5774608829631843.0 / 268435456.0, + -7645843051051357.0 / 8589934592.0 + }; + final double[] b = new double[] { + -5712344449280879.0 / 2097152.0, + -4550117129121957.0 / 2097152.0, + 8846951984510141.0 / 131072.0 + }; + + double[] scaledA = new double[a.length]; + double[] scaledB = new double[b.length]; + for (int i = 0; i < scaledA.length; ++i) { + scaledA[i] = FastMath.scalb(a[i], -scale); + scaledB[i] = FastMath.scalb(b[i], +scale); + } + final double abSumInline = MathArrays.linearCombination(scaledA[0], scaledB[0], + scaledA[1], scaledB[1], + scaledA[2], scaledB[2]); + final double abSumArray = MathArrays.linearCombination(scaledA, scaledB); + + Assert.assertEquals(abSumInline, abSumArray, 0); + Assert.assertEquals(-1.8551294182586248737720779899, abSumInline, 1.0e-15); + + final double naive = scaledA[0] * scaledB[0] + scaledA[1] * scaledB[1] + scaledA[2] * scaledB[2]; + Assert.assertTrue(FastMath.abs(naive - abSumInline) > 1.5); + + } + @Test public void testLinearCombinationInfinite() { final double[][] a = new double[][] {