You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by lu...@apache.org on 2014/06/13 16:20:05 UTC
svn commit: r1602438 - in /commons/proper/math/trunk/src:
changes/changes.xml main/java/org/apache/commons/math3/util/Precision.java
test/java/org/apache/commons/math3/util/PrecisionTest.java
Author: luc
Date: Fri Jun 13 14:20:05 2014
New Revision: 1602438
URL: http://svn.apache.org/r1602438
Log:
Fixed overflow in Precision.equals with ulps.
Both double and float versions were affected.
JIRA: MATH-1127
Modified:
commons/proper/math/trunk/src/changes/changes.xml
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Precision.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PrecisionTest.java
Modified: commons/proper/math/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/changes/changes.xml?rev=1602438&r1=1602437&r2=1602438&view=diff
==============================================================================
--- commons/proper/math/trunk/src/changes/changes.xml (original)
+++ commons/proper/math/trunk/src/changes/changes.xml Fri Jun 13 14:20:05 2014
@@ -73,6 +73,9 @@ Users are encouraged to upgrade to this
2. A few methods in the FastMath class are in fact slower that their
counterpart in either Math or StrictMath (cf. MATH-740 and MATH-901).
">
+ <action dev="luc" type="fix" issue="MATH-1127">
+ Fixed overflow in Precision.equals with ulps (both double and float versions).
+ </action>
<action dev="tn" type="fix" issue="MATH-1125" due-to="Ajo Fod">
Performance improvements for Student's t-distribution.
</action>
Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Precision.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Precision.java?rev=1602438&r1=1602437&r2=1602438&view=diff
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Precision.java (original)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Precision.java Fri Jun 13 14:20:05 2014
@@ -62,6 +62,14 @@ public class Precision {
private static final int SGN_MASK_FLOAT = 0x80000000;
/** Positive zero. */
private static final double POSITIVE_ZERO = 0d;
+ /** 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);
+ /** Positive zero bits. */
+ private static final int POSITIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(+0.0f);
+ /** Negative zero bits. */
+ private static final int NEGATIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(-0.0f);
static {
/*
@@ -109,7 +117,7 @@ public class Precision {
* (or fewer) floating point numbers between them, i.e. two adjacent floating
* point numbers are considered equal.
* Adapted from <a
- * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm">
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
* Bruce Dawson</a>
*
* @param x first value
@@ -190,7 +198,7 @@ public class Precision {
* (or fewer) floating point numbers between them, i.e. two adjacent floating
* point numbers are considered equal.
* Adapted from <a
- * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm">
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
* Bruce Dawson</a>
*
* @param x first value
@@ -201,21 +209,37 @@ public class Precision {
* point values between {@code x} and {@code y}.
* @since 2.2
*/
- public static boolean equals(float x, float y, int maxUlps) {
- int xInt = Float.floatToIntBits(x);
- int yInt = Float.floatToIntBits(y);
-
- // Make lexicographically ordered as a two's-complement integer.
- if (xInt < 0) {
- xInt = SGN_MASK_FLOAT - xInt;
- }
- if (yInt < 0) {
- yInt = SGN_MASK_FLOAT - yInt;
- }
+ public static boolean equals(final float x, final float y, final int maxUlps) {
+
+ final int xInt = Float.floatToRawIntBits(x);
+ final int yInt = Float.floatToRawIntBits(y);
+
+ final boolean isEqual;
+ if (((xInt ^ yInt) & SGN_MASK_FLOAT) == 0) {
+ // number have same sign, there is no risk of overflow
+ isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ } else {
+ // number have opposite signs, take care of overflow
+ final int deltaPlus;
+ final int deltaMinus;
+ if (xInt < yInt) {
+ deltaPlus = yInt - POSITIVE_ZERO_FLOAT_BITS;
+ deltaMinus = xInt - NEGATIVE_ZERO_FLOAT_BITS;
+ } else {
+ deltaPlus = xInt - POSITIVE_ZERO_FLOAT_BITS;
+ deltaMinus = yInt - NEGATIVE_ZERO_FLOAT_BITS;
+ }
- final boolean isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ if (deltaPlus > maxUlps) {
+ isEqual = false;
+ } else {
+ isEqual = deltaMinus <= (maxUlps - deltaPlus);
+ }
+
+ }
return isEqual && !Float.isNaN(x) && !Float.isNaN(y);
+
}
/**
@@ -315,12 +339,16 @@ public class Precision {
/**
* Returns true if both arguments are equal or within the range of allowed
* error (inclusive).
+ * <p>
* Two float numbers are considered equal if there are {@code (maxUlps - 1)}
- * (or fewer) floating point numbers between them, i.e. two adjacent floating
- * point numbers are considered equal.
+ * (or fewer) floating point numbers between them, i.e. two adjacent
+ * floating point numbers are considered equal.
+ * </p>
+ * <p>
* Adapted from <a
- * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm">
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
* Bruce Dawson</a>
+ * </p>
*
* @param x first value
* @param y second value
@@ -329,21 +357,37 @@ public class Precision {
* @return {@code true} if there are fewer than {@code maxUlps} floating
* point values between {@code x} and {@code y}.
*/
- public static boolean equals(double x, double y, int maxUlps) {
- long xInt = Double.doubleToLongBits(x);
- long yInt = Double.doubleToLongBits(y);
-
- // Make lexicographically ordered as a two's-complement integer.
- if (xInt < 0) {
- xInt = SGN_MASK - xInt;
- }
- if (yInt < 0) {
- yInt = SGN_MASK - yInt;
- }
+ public static boolean equals(final double x, final double y, final int maxUlps) {
+
+ final long xInt = Double.doubleToRawLongBits(x);
+ final long yInt = Double.doubleToRawLongBits(y);
+
+ final boolean isEqual;
+ if (((xInt ^ yInt) & SGN_MASK) == 0l) {
+ // number have same sign, there is no risk of overflow
+ isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ } else {
+ // number have opposite signs, take care of overflow
+ final long deltaPlus;
+ final long deltaMinus;
+ if (xInt < yInt) {
+ deltaPlus = yInt - POSITIVE_ZERO_DOUBLE_BITS;
+ deltaMinus = xInt - NEGATIVE_ZERO_DOUBLE_BITS;
+ } else {
+ deltaPlus = xInt - POSITIVE_ZERO_DOUBLE_BITS;
+ deltaMinus = yInt - NEGATIVE_ZERO_DOUBLE_BITS;
+ }
+
+ if (deltaPlus > maxUlps) {
+ isEqual = false;
+ } else {
+ isEqual = deltaMinus <= (maxUlps - deltaPlus);
+ }
- final boolean isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ }
return isEqual && !Double.isNaN(x) && !Double.isNaN(y);
+
}
/**
Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PrecisionTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PrecisionTest.java?rev=1602438&r1=1602437&r2=1602438&view=diff
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PrecisionTest.java (original)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PrecisionTest.java Fri Jun 13 14:20:05 2014
@@ -540,4 +540,13 @@ public class PrecisionTest {
// b) 1 + "the number after EPSILON" is not equal to 1.
Assert.assertFalse(1 + afterEpsilon == 1);
}
+
+ @Test
+ public void testMath1127() {
+ Assert.assertFalse(Precision.equals(2.0, -2.0, 1));
+ Assert.assertTrue(Precision.equals(0.0, -0.0, 0));
+ Assert.assertFalse(Precision.equals(2.0f, -2.0f, 1));
+ Assert.assertTrue(Precision.equals(0.0f, -0.0f, 0));
+ }
+
}