From ceacae9a3c1092c2a9fcb667331348cb887cf407 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe Date: Wed, 29 Apr 2009 18:33:59 +0000 Subject: [PATCH] Added an utility equality method between double numbers using tolerance in ulps (Units in Last Position) JIRA: MATH-264 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@769867 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/commons/math/util/MathUtils.java | 36 +++++++++++++++++++ src/site/xdoc/changes.xml | 4 +++ .../commons/math/util/MathUtilsTest.java | 25 +++++++++++++ 3 files changed, 65 insertions(+) diff --git a/src/java/org/apache/commons/math/util/MathUtils.java b/src/java/org/apache/commons/math/util/MathUtils.java index 203477505..abb5e9158 100644 --- a/src/java/org/apache/commons/math/util/MathUtils.java +++ b/src/java/org/apache/commons/math/util/MathUtils.java @@ -59,6 +59,9 @@ public final class MathUtils { /** 2 π. */ private static final double TWO_PI = 2 * Math.PI; + private static final int NAN_GAP = 4 * 1024 * 1024; + private static final long SGN_MASK = 0x8000000000000000L; + /** * Private Constructor */ @@ -406,6 +409,39 @@ public final class MathUtils { return equals(x, y) || (Math.abs(y - x) <= eps); } + /** + * Returns true iff both arguments are equal or within the range of allowed + * error (inclusive). + * Adapted from + * Bruce Dawson + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if there are less than {@code maxUlps} floating + * point values between {@code x} and {@code y} + */ + public static boolean equals(double x, double y, int maxUlps) { + // Check that "maxUlps" is non-negative and small enough so that the + // default NAN won't compare as equal to anything. + assert maxUlps > 0 && maxUlps < NAN_GAP; + + 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; + } + + return Math.abs(xInt - yInt) <= maxUlps; + } + /** * Returns true iff both arguments are null or have same dimensions * and all their elements are {@link #equals(double,double) equals} diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 3bf57bf16..cb7340462 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -39,6 +39,10 @@ The type attribute can be add,update,fix,remove. + + Added an utility equality method between double numbers using tolerance + in ulps (Units in Last Position) + Added support for any type of field in linear algebra (FielxMatrix, FieldVector, FieldLUDecomposition) diff --git a/src/test/org/apache/commons/math/util/MathUtilsTest.java b/src/test/org/apache/commons/math/util/MathUtilsTest.java index 72fe05188..b8ac7935e 100644 --- a/src/test/org/apache/commons/math/util/MathUtilsTest.java +++ b/src/test/org/apache/commons/math/util/MathUtilsTest.java @@ -353,6 +353,31 @@ public final class MathUtilsTest extends TestCase { assertFalse(MathUtils.equals(153.0000, 153.0625, .0624)); assertFalse(MathUtils.equals(152.9374, 153.0000, .0625)); } + + public void testEqualsWithAllowedUlps() { + assertTrue(MathUtils.equals(153, 153, 1)); + + assertTrue(MathUtils.equals(153, 153.00000000000003, 1)); + assertFalse(MathUtils.equals(153, 153.00000000000006, 1)); + assertTrue(MathUtils.equals(153, 152.99999999999997, 1)); + assertFalse(MathUtils.equals(153, 152.99999999999994, 1)); + + assertTrue(MathUtils.equals(-128, -127.99999999999999, 1)); + assertFalse(MathUtils.equals(-128, -127.99999999999997, 1)); + assertTrue(MathUtils.equals(-128, -128.00000000000003, 1)); + assertFalse(MathUtils.equals(-128, -128.00000000000006, 1)); + + assertTrue(MathUtils.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1)); + assertTrue(MathUtils.equals(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1)); + + assertTrue(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1)); + assertTrue(MathUtils.equals(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1)); + + + assertTrue(MathUtils.equals(Double.NaN, Double.NaN, 1)); + + assertFalse(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000)); + } public void testArrayEquals() { assertFalse(MathUtils.equals(new double[] { 1d }, null));