5

Is it safe if I use comparision like this (a is int, b and c is float/double):

a == b
b == c

It may hear ridiculous, but in my old programing language, sometimes 1 + 2 == 3 is false (because left side returns 2.99999999999...). And, what about this:

Math.sqrt(b) == Math.sqrt(c)
b / 3 == 10 / 3 //In case b = 10, does it return true?
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
Luke Vo
  • 17,859
  • 21
  • 105
  • 181
  • 3
    If 1.0 + 2.0 != 3.0, regardless of precision, your computer is broken. Now if 0.1 + 0.2 != 0.3, that makes more sense. – cHao Nov 14 '11 at 09:17

10 Answers10

14

In general, no it is not safe due to the fact that so many decimal numbers cannot be precisely represented as float or double values. The often stated solution is test if the difference between the numbers is less than some "small" value (often denoted by a greek 'epsilon' character in the maths literature).

However - you need to be a bit careful how you do the test. For instance, if you write:

if (Math.abs(a - b) < 0.000001) {
    System.err.println("equal");
}

where a and b are supposed to be "the same", you are testing the absolute error. If you do this, you can get into trouble if a and b are (say_ 1,999,999.99 and 2,000,000.00 respectively. The difference between these two numbers is less than the smallest representable value at that scale for a float, and yet it is much bigger than our chosen epsilon.

Arguably, a better approach is to use the relative error; e.g. coded (defensively) as

if (a == b ||
    Math.abs(a - b) / Math.max(Math.abs(a), Math.abs(b)) < 0.000001) {
    System.err.println("close enough to be equal");
}

But even this is not the complete answer, because it does not take account of the way that certain calculations cause the errors to build up to unmanageable proportions. Take a look at this Wikipedia link for more details.

The bottom line is that dealing with errors in floating point calculations is a lot more difficult than it appears at first glance.


The other point to note is (as others have explained) integer arithmetic behaves very differently to floating point arithmetic in a couple of respects:

  • integer division will truncate if the result is not integral
  • integer addition subtraction and multiplication will overflow.

Both of these happen without any warning, either at compile time or at runtime.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
5

You do need to exercise some care.

1.0 + 2.0 == 3.0

is true because integers are exactly representable.

Math.sqrt(b) == Math.sqrt(c) 

if b == c.

b / 3.0 == 10.0 / 3.0

if b == 10.0 which is what I think you meant.

The last two examples compare two different instances of the same calculation. When you have different calculations with non representable numbers then exact equality testing fails.

If you are testing the results of a calculation that is subject to floating point approximation then equality testing should be done up to a tolerance.

Do you have any specific real world examples? I think you will find that it is rare to want to test equality with floating point.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Yes, it may be rare, but when I made a school score calculator program in VB.NET, 1 + 2 sudden return 2.99999, which make all logic become false. And rare is unacceptable here. – Luke Vo Jul 22 '11 at 06:25
  • since 1 and 2 are representable that is hard to explain. – David Heffernan Jul 22 '11 at 06:28
  • Actually, 2 come from 10 / 5 (exactly 10 / 5), but somehow, Double in VB.NET return 1.9999999 so when + 1, it is 2.9999999999999. – Luke Vo Jul 22 '11 at 06:30
  • Whilst you are wise to be cautious about how floating point works, you should not be paranoid. `10.0/5.0` results in `2.0`, represented exactly in VB.NET and every other environment based on IEEE754 floating point. What's more `1.0+2.0==3.0` in such environments. It's important to know what you can do as well as what you can't do, and where the boundaries lie. – David Heffernan Jul 22 '11 at 08:05
  • Well, I'm not very sure about 5, I just sure that `2` is the result of a division of `10` with a `number` (comes from a function that other teammate does, not me), and in theory/algorithm, it should be integer (although the return type is double). So maybe the problem come from that function. – Luke Vo Jul 22 '11 at 09:07
  • Integers are exactly representable. In fact, rational numbers whose denominators are powers of 2 are exactly representable. So, 1/2, 3/16 are exactly representable, but 1/3 is not. – David Heffernan Jul 22 '11 at 09:22
  • *"In fact, rational numbers whose denominators are powers of 2 are exactly representable."* .... provided there is enough precision in `float` or `double` for the specific rational. Consider (2^100 - 1) / 2^100 – Stephen C Jun 19 '17 at 23:30
4

b / 3 != 10 / 3 - if b is a floating point variable like b = 10.0f, so b / 3 is 3.3333, while 10 / 3 is integer division, so is equal to 3.

If b == c, then Math.sqrt(b) == Math.sqrt(c) - this is because the sqrt function returns double anyways.

In general, you shouldn't be comparing doubles/floats for equation, because they are floating point numbers so you might get errors. You almost always want to compare them with a given precision, i.e.:

b - c < 0.000001

Jesse Webb
  • 43,135
  • 27
  • 106
  • 143
Petar Ivanov
  • 91,536
  • 11
  • 82
  • 95
4

The safest way to compare a float/double with something else is actually to use see if their difference is a small number.

e.g.

Math.abs(a - b) < EPS

where EPS can be something like 0.0000001.

In this way you make sure that precision errors do not affect your results.

Ionel Gog
  • 326
  • 2
  • 4
2

== comparison is not particularly safe for doubles/floats in basically any language. An epsilon comparison method (where you check that the difference between two floats is reasonably small) is your best bet.

For:

Math.sqrt(b) == Math.sqrt(c)

I'm not sure why you wouldn't just compare b and c, but an epsilon comparison would work here too.

For:

b / 3 == 10 / 3

Since 10/3 = 3 because of integer division, this will not necessarily give the results you're looking for. You could use 10.0 / 3, though i'm still not sure why you wouldn't just compare b and 10 (using the epsilon comparison method in either case).

Jodaka
  • 1,237
  • 8
  • 16
  • It's just an example, sometimes you will need a function (not sqrt) that return a double/float value to compare. – Luke Vo Jul 22 '11 at 06:29
  • Gotcha. Regardless, you should use the comparison method that Ionel demonstrates in his answer. – Jodaka Jul 22 '11 at 06:32
1

Float wrapper class's compare method can be used to compare two float values.

Float.compare(float1,float2)==0

It will compare integer bits corresponding to each float object.

Sanjeev Kumar
  • 680
  • 8
  • 16
0

In java you can compare a float with another float,and a double with another double.And you will get true when comparing a double with a float when their precision is equal

Rasel
  • 15,499
  • 6
  • 40
  • 50
0

b is float and 10 is integer then if you compare both with your this critearia then it will give false because...

b = flaot (meance ans 3.33333333333333333333333)
10 is integer so that make sence.
Siten
  • 4,515
  • 9
  • 39
  • 64
0

If you want a more complete explanation, here there is a good one: http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html (but it is a little bit long)

Pescuma
  • 2,044
  • 1
  • 12
  • 5
0

Efficient way for 2 digits precision:

public static void main(String[] args) {
    double number = 3.14999965359;
    float number2 = 3.14909265359f;
    Date date = new Date();
    System.out.println(equalsDoubleWithFloat(number, number2));
    System.out.println(isDoubleBigFromFloat(number, number2));
    System.out.println("delay: " + (new Date().getTime() - date.getTime()) + " ms");
}

private static boolean equalsDoubleWithFloat(double db, float fl) {
    return Float.compare((float) db, fl) == 0 || Math.abs(db - fl) < 0.01;
}

private static boolean isDoubleBigFromFloat(double db, float fl) {
    return Math.abs(db - fl) > 0.01 && Float.compare((float) db, fl) > 0;
}
public static boolean isDoubleSmallFromFloat(double db, float fl) {
        return Math.abs(db - fl) > 0.01 && Float.compare((float) db, fl) < 0;
}

Output is:

true
false
delay: 1 ms

other solution:

private static final DecimalFormat TWO_DIGITS = new DecimalFormat("0.00");

public static double formatDouble(double value) {
    return Double.parseDouble(TWO_DIGITS.format(value));
}

public static float formatFloat(float value) {
    return Float.parseFloat(TWO_DIGITS.format(value));
}

public static int compareDoubleWithFloat(double db, float fl) {
    double roundedDouble = formatDouble(db);
    float roundedFloat = formatFloat(fl);
    if (Math.abs(roundedDouble - roundedFloat) < 0.01) {
        return 0;
    } else if (roundedDouble > roundedFloat) {
        return 1;
    } else {
        return -1;
    }
}
Bilal Demir
  • 587
  • 6
  • 17