1

Why floating point comparisons are inaccurate and should be avoided?

For eg. this returns false

2.0 * 5.0 == 10.0


PS : By comparisons I mean equality and inequality, not < and >.

3 Answers 3

2

As pointed out by other answers, your example returns true and you should deal with floats carefully because they are NOT precise.

The precision problem is caused by the fact that there are some floating-point values that cannot be exactly represented in binary. The decimal numbering system has this problem too with some values.

For example, the value 1/3 (or 0.333...) can never be represented precisely as a floating-point value because obviously it requires an infinite number of digits after the decimal point. A value like 1/2 on the other hand can be precisely represented as 0.5.

In the decimal numbering system, the position of the first digit after the decimal point is 10^-1, the following position is 10^-2, and so on. For example, 1/2 (dec) = 5/10 (dec) = 5 * 10^-1 (dec) = 0.5 (dec).

Similarly, in the binary numbering system, the position of the first digit after the floating point is 2^-1, the following position 2^-2 and so on. So 1/2 (dec) = 1 * 2^-1 (dec) = 0.1 (bin).

As you might have noticed, you should be able to represent a fractional value a/b (dec) as c/10^d (dec) to be able to represent it precisely as a floating-point value in decimal, where a, b, c, and d are all integers.

Similarly, you should be able to represent the value a/b (dec) as f/2^g (dec), to be able to represent it as a floating-point value precisely in binary, where a, b, f, and g are all integers.

For example, since you can't represent a value like 1/10 (dec) or 0.1 (dec) as f/2^g (dec), because obviously you can't represent 10 (dec) as 2^g (dec), you can't represent that precisely as a floating-point value in binary.

This imprecision might lead to serious problems and that's why you should be careful when dealing with floating-point values.

1

First things first, your example is returning true.

It is difficult to deal with floating point numbers in any programming language unless you know how they are stored/operated on your machine. Although example you gave, returned true on ideone, but it is for sure that there could be any another example for such weird behaviour.

float a = 0.1;
printf("%.18f", a);

yields 0.100000001490116 as output.

Errors can also be amplified as

float a = 0.000001, sum = 0.0;
for (i = 0 ; i < 1000000 ; ++i)
    sum += a;
printf("%.18f", sum);

yields 1.009038925170898438 as output.

This is because floating point numbers are not stored the way simply as integers are. C99 follows IEEE 754-1985(IEC 60559) standard to deal with floating point numbers.

There are 2 types of numbers, namely single precision and double precision.

 --------------------------------------------------------------------------------------
|      Level         |      Width      |                   Range                       |
|--------------------+-----------------+-----------------------------------------------
|                    |                 |                                               |
| Single Precision   |      32-bits    |       ±1.18*(10**(-38)) to ±3.4*(10**(38))    |
|                    |                 |                                               |
| Double Precision   |      64-bits    |      ±2.23*(10**(-308)) to ±1.80*(10**(308))  |
 --------------------+-----------------+-----------------------------------------------

In memory, these numbers are stored in 3 fields :

  • Signed bit
  • Exponent (Biased)
  • Mantissa (Fractional Part)

Single precision floating point numbers consists of 32-bits, out of which 1 bit is dedicated to store the sign, 8 bits for exponent, and the rest(23 bits) for mantissa. For the double precision floating point numbers, out of 64 bits, there is 1 sign bit, 11 bits for exponent, and 52 bits for storing mantissa.

Now, when you want to store a number, the number is first converted to its binary equivalent and then changed to a notation such that there remains only a single bit on left side of the period.

Say you got 0.15625 in base 10, then its binary equivalent comes out to be 0.00101, which is then written as 1.01 * 2-3. In this case, the sign bit is set to 0 as the number is positive. For a single precision number, we have 8 bits, since we need to manage both positive and negative (and 0) values in this, lets set 28/2 - 1 = 127 as the bias. A similar calculation for double precision number gives 211/2 - 1 = 1023 as the bias. The biased exponent is the sum of this bias and the exponent from the notation. In the above example, the biased exponent is 127 + (-3) = 124 for single precision and 1023 + (-3) = 1020 for double precision. The fractional part is stored as 0.0100...

In short, numbers aren't stored directly, but in a different notation. Conversion to and/or from this notation to original values may result in loss of precision. This is because approximations of those numbers are stored. However, it should be noted that real values to approximated values are like many to one mapping, So it possible that two different numbers are approximated to a same value and when retrieved back, result a same number(unlike expectation). On the same time, this also points to the fact that both numbers should lie in a particular range. Hence, all numbers in a particular range approximate to a same number and when retrieved back, result to a same value for all. For instance, consider this

float a = 0.1, b = 0.100000005;
printf("%.18f %.18f", a, b);

results in 0.100000001490116119 0.100000001490116119.

Therefore, all numbers lying in the range [0.1, 0.100000005] will be stored as same number, and expressions like 0.1 == 0.100000005 return true. That is why it is advised not to use == and != operator with floating point numbers. Instead, one could check range in such situation using > and/or < against a fixed precision.


Additional Readings :

  1. What Every Computer Scientist Should Know About Floating-Point Arithmetic
  2. Wikipedia : IEEE 754-1985
  3. Online IEEE-754 Calculator

Most of the stuff has been taken from Wikipedia.

a**b represents ab.

0

Is this a question or a statement?

If a question, then this short might help: https://www.youtube.com/watch?v=JqKDFhCaWC8

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .