-2

in my java book it told me to not directly compare 2 different float(type) numbers when storing them in variables. Because it gives an approx of a number in the variable. Instead it suggested checking the absolute value of the difference and see if it equals 0. If it does they are the same. How is this helpful? What if I store 5 in variable a and 5 in variable b, how can they not be the same? And how does it help if I compare absolute value??

double a=5,b=5;

if (Math.abs(a-b)==0)
//run code

if (a==b)
//run code 

I don't see at all why the above method would be more accurate? Since if 'a' is not equal to 'b' it wont matter if I use Math.abs.

I appreciate replies and thank you for your time.

I tried both methods.

owns good
  • 3
  • 2
  • 3
    The value in a floating point variable is the value that's stores in the variable, fetching it will not make the value better or worse. So "it gives an approx of a number in the variable" is not true. What is true is that doing operations on floating point values (addition, multiplication, division, etc) *can* (but not always, depends on the values) lead to small rounding errors. The more operations you make of the values the larger the error becomes. So after doing a few operations of the variables, then a direct equality comparison might not lead to expected results. – Some programmer dude Oct 26 '22 at 11:13
  • 1
    The solution is usually to compare using an *epsilon*. similar to your `abs` method: `if (Math.abs(a - b) < 0.00001)` – Some programmer dude Oct 26 '22 at 11:15
  • 1
    Also see [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Some programmer dude Oct 26 '22 at 11:15
  • Oh! Yeah that is much more clever. Like setting to <0.0001 Very well done. That means even if there are minor errors along the way this still is not that big of a diff most of the time. I see, so instead of settiing ==0 set to <0.0001. Why didn't I think of that. Thank you ! – owns good Oct 26 '22 at 11:27
  • Both methods are weak. – chux - Reinstate Monica Nov 01 '22 at 20:26

2 Answers2

2

Inaccuracy with comparisons using the == operator is caused by the way double values are stored in a computer's memory. We need to remember that there is an infinite number of values that must fit in limited memory space, usually 64 bits. As a result, we can't have an exact representation of most double values in our computers. They must be rounded to be saved.

Because of the rounding inaccuracy, interesting errors might occur:

double d1 = 0;
for (int i = 1; i <= 8; i++) {
d1 += 0.1;
}

double d2 = 0.1 * 8;

System.out.println(d1);
System.out.println(d2);

Both variables, d1 and d2, should equal 0.8. However, when we run the code above, we'll see the following results:

0.7999999999999999
0.8

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.

If we want to have the best precision and control over the rounding mechanism, we can use java.math.BigDecimal class.

The recommended algorithm to compare double values in plain Java is a threshold comparison method. In this case, we need to check whether the difference between both numbers is within the specified tolerance, commonly called epsilon:

double epsilon = 0.000001d;

assertThat(Math.abs(d1 - d2) < epsilon).isTrue();

The smaller the epsilon's value, the greater the comparison accuracy. However, if we specify the tolerance value too small, we'll get the same false result as in the simple == comparison

Ali Hamed
  • 316
  • 1
  • 1
  • 12
  • Welcome to SO, @AliHamed; a fine answer. A few highlights just to make things clear for the asker: _Instead it suggested checking the absolute value of the difference and see if it equals 0_ - I doubt it did that; you check the absvalue of the difference and see if it is a very small number, as this answer shows. Not 0. _Because it gives an approx of a number in the variable_ Some numbers. '5' can be stored perfectly. 0.1 cannot. Every operation rounds to the nearest representable. Do a lot of operations, as this answer does - and the rounding gets you. – rzwitserloot Oct 26 '22 at 12:07
  • `Math.abs(d1 - d2) < epsilon` takes the usefulness of floating out of `double`. When `d1` and `d2` are consecutive `double`, and large like 1e20, `Math.abs(d1 - d2) < epsilon` is always false. When `d1` and `d2` are not anywhere near consecutive `double`, and tiny like 1e-20, `Math.abs(d1 - d2) < epsilon` is always true. `epsilon` should scale with the magnitudes of the values involved. – chux - Reinstate Monica Nov 01 '22 at 20:24
1

The thing is, That statement you read in your java book just prevents you from some errors in future, that can be hardly debugged. Computers store decimals/floats as binary, thus not everything we can express as rational in decimal numbers can be expressed as rational in binary, so there's always something like 0.7 = 0.699999999998511. You may not see difference in those comparisons you use, but in real project where you may use much more variables, add and subtract from them, this difference may appear in very surprising place.

There is some classic question about floating numbers. You may see it in any language as well Why does this code print a result of '7'?