Performant way to check java.lang.Double for equality
Asked Answered
S

3

3

What is the most performant way to check double values for equality.

I understand that

double a = 0.00023d;
double b = 0.00029d;

boolean eq = (a == b);

is slow.

So I'm using

double epsilon = 0.00000001d;
eq = Math.abs(a - b) < epsilon;

The problem is that Infinitest is complaning about tests taking too much time. It's not a big deal (1 sec top), but it made me curious.

Additional info

a is hard coded since it's the expected value, b is computed by

  // fyi: current = int, max = int
  public double getStatus()
  {
    double value = 0.0;
    if (current != 0 && max != 0)
      value = ((double) current) / max;
    return value;
  }

Update

java.lang.Double does it that way

public boolean equals(Object obj) {
return (obj instanceof Double)
       && (doubleToLongBits(((Double)obj).value) ==
          doubleToLongBits(value));
}

so one could assume that is the best practice.

Strahan answered 31/7, 2013 at 13:4 Comment(5)
Just how many values are you comparing, if you're taking nearly a second? And do you need to use Double instead of double?Gentlemanly
Actually I don't, I just did it in the example I posted here. Sry! I'll fix it. Just 2 values. The rest is some initialization, that I also do in some other test methods.Strahan
boolean eq = (a == b) takes less than 10 nano-seconds. How faster do you need it to be?Angora
Using Double is much slower than than double and doesn't behave the same as double for equals so that is why the code is different.Angora
Do you know how the primitive type double behaves? Or is that some kind of platform dependent native stuff?Strahan
P
6

JUnit has a method of checking a Double for 'equality' with a given delta:

Assert.assertEquals(0.00023d, 0.00029d, 0.0001d);

See this API documentation.

As noted in the comments, JUnit actually most likely is slower than comparing manually with a given delta. JUnit first does a Double.compare(expected, actual) followed (if not equal) by a Math.abs(expected - actual) <= delta.

Hopefully this answer still is useful for people not aware that JUnit actually provides a way for inexact Double equality testing.

Pleasant answered 31/7, 2013 at 13:8 Comment(5)
Thank you! Unfortunatelly the API doc says nothing about the intrinsics.Strahan
Grepcode org.junit.Assert. It's the combination of your "slow" test done first, followed by your "fast" test. You won't learn anything from this.Tate
JUnit first does Double.compare and than checks for Math.abs(expected - actual) <= delta. So I guess it's faster to use the delta approach right away, if one does not need 100% equality or transitivity.Strahan
Did not realize JUnit works the way it does, yes I recon doing it directly would be faster. However, does Infinitest count the time spent in JUnit code? Well, at least you now know about this JUnit method :) I'll update my answer to reflect the way JUnit checks doubles.Pleasant
Yes, JUnit measures the time. At least the time spent in the @Test annotated methods. Of course, thank you. And the method JUnit provides is more convenient :)Strahan
L
2

Actually, comparing two float/double values for equality is a bad practice in itself, because floating point numbers suffer from rounding errors. Two numbers which would be equal in symbolic maths may be different to the computer, depending on how they are computed.

The best practice is the second option you used: Math.abs(a - b) < epsilon.
I understand it may be slower than you'd like, but it's the right way to do it. Bitwise comparison may result in two numbers being considered as different even though they're the same, from the point of view of the application (they would be equal if you had computed them by hand, but they are bitwise different due to rounding errors).

Leatherleaf answered 6/8, 2013 at 13:21 Comment(0)
S
0

java.lang.Double

As shown in the question java.lang.Double.equals() calls public static long doubleToLongBits(double value), which

/**
 * Returns a representation of the specified floating-point value
 * according to the IEEE 754 floating-point "double
 * format" bit layout.

and then checks for equality with ==. (doubleToLongBits internally calls public static native long doubleToRawLongBits(double value), so it is platform dependent).

Here the way the primitive type works.

primitive type double

The floating-point types are float and double, which are conceptually associated with the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).JLS-4.2.3


Operators on floating-point numbers behave as specified by IEEE 754 (with the exception of the remainder operator (§15.17.3)). JLS-4.2.4


So the fastest way would be using primitive types and possibly performing a 'delta check' depending on the needed accuracy. If that's not possible using the methods provided by Double.

One should not use the JUnit assert method, since it performs more checks, one would be better off doing sth like:

boolean eq = Double.valueOf(5.0d).equals(Double.valueOf(2.0d));
Assert.assertTrue(eq);
Strahan answered 31/7, 2013 at 13:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.