Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

signed zero double equals (as in ==) but Double.compare(double,double) != 0

Tags:

java

I've encountered something strange regarding comparing double zeros. Depending on how the double zero primitives are initiated the Double.compare(double,double) method may or may not think that they are "equal" (may or may not return 0).

Comparing the various zero double:s using == always reports them as equal. If they are equal in terms of ==, they must (should) be equal in terms of the the compare method. They are not!

Check out this sample program:

public class CompareZeros {

public static void main(final String[] args) {

    final double negDbl = -0.0;
    final double posInt = 0;
    final double posDbl = 0.0;
    final double negInt = -0;

    CompareZeros.compare("negDbl <-> posInt", negDbl, posInt);
    CompareZeros.compare("negDbl <-> posDbl", negDbl, posDbl);
    CompareZeros.compare("negDbl <-> negInt", negDbl, negInt);

    CompareZeros.compare("posInt <-> negDbl", posInt, negDbl);
    CompareZeros.compare("posInt <-> posDbl", posInt, posDbl);
    CompareZeros.compare("posInt <-> negInt", posInt, negInt);

    CompareZeros.compare("posDbl <-> negDbl", posDbl, negDbl);
    CompareZeros.compare("posDbl <-> posInt", posDbl, posInt);
    CompareZeros.compare("posDbl <-> negInt", posDbl, negInt);

    CompareZeros.compare("negInt <-> negDbl", negInt, negDbl);
    CompareZeros.compare("negInt <-> posInt", negInt, posInt);
    CompareZeros.compare("negInt <-> posDbl", negInt, posDbl);

}

static void compare(final String id, final double arg0, final double arg1) {

    System.out.print(id + ": ");

    if (arg0 == arg1) {
        if (Double.compare(arg0, arg1) == 0) {
            System.out.println("OK");
        } else {
            System.out.println("Strange, and must be wrong!");
        }
    } else {
        if (Double.compare(arg0, arg1) == 0) {
            System.out.println("Strange, but perhaps logically ok");
        } else {
            System.out.println("Consistent...");
        }
    }

}
}

It outputs this:

negDbl <-> posInt: Strange, and must be wrong!
negDbl <-> posDbl: Strange, and must be wrong!
negDbl <-> negInt: Strange, and must be wrong!
posInt <-> negDbl: Strange, and must be wrong!
posInt <-> posDbl: OK
posInt <-> negInt: OK
posDbl <-> negDbl: Strange, and must be wrong!
posDbl <-> posInt: OK
posDbl <-> negInt: OK
negInt <-> negDbl: Strange, and must be wrong!
negInt <-> posInt: OK
negInt <-> posDbl: OK
like image 247
apete Avatar asked Aug 07 '17 09:08

apete


People also ask

Can you use == to compare doubles?

Using the == Operator As a result, we can't have an exact representation of most double values in our computers. They must be rounded to be saved. In that case, comparing both values with the == operator would produce a wrong result. For this reason, we must use a more complex comparison algorithm.

How do you check if a double value is greater than zero in C#?

compare(employeeSalary, Double. valueOf(0.0)) > 0 ){ // Employee salary is greater than zero. } else{ // Employee salary is less than or equal to zero. }


1 Answers

I checked the source of Double.compare in the JDK that I have. It looks like this:

public static int compare(double d1, double d2) {
    if (d1 < d2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (d1 > d2)
        return 1;            // Neither val is NaN, thisVal is larger

    // Cannot use doubleToRawLongBits because of possibility of NaNs.
    long thisBits    = Double.doubleToLongBits(d1);
    long anotherBits = Double.doubleToLongBits(d2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

The last few lines explain why this happens. The two parameters are converted into long bits. These bits are then compared. -0.0 and 0.0 are represented differently in bits. The long value for 0.0 is larger than -0.0. The former is 0 while the latter is -9223372036854775808.

The docs also says so:

0.0d is considered by this method to be greater than -0.0d.

Why is -0 different then?

This is because -0 is the negation operator applied to an integer literal 0, which evaluates to 0. The 0 is then converted implicitly into a double.

As to why == thinks negative zero and positive zero are the same, it is specified very clearly in the JLS - section 15.21.1:

15.21.1. Numerical Equality Operators == and !=

...

Floating-point equality testing is performed in accordance with the rules of the IEEE 754 standard:

...

  • Positive zero and negative zero are considered equal.
like image 70
Sweeper Avatar answered Nov 15 '22 05:11

Sweeper