The problem here is that .1
, .2
and .3
are not finite digit numbers in base 2
, but are periodic in their binary representation.
I'll try to illustrate: Let's suppose we are trying to use base 3 numbering system, and try to sum .1
(which is one third) + .1
(another third) and + .1
(which is another third), but instead of doing that in base 3, let's do it in base 10.
.1
in base 3, converts to base ten as .3333333333333333
(with 16 base ten digits), and if you try to add it three times, you'll get .9999999999999999
and not 1.0
.
There's no possibility here to solve to this problem, but cutting the precission on the result. If we have 10 digits, and try to add .3333333333
three times... we'll get .9999999999
(with ten digits precission) the solution here is to cut the result to nine digits and round it (in this case 1.000000000
) but if you substract this number to the correct one (1.000000000 - 0.9999999999
) you'll get again 0.0000000001
.
This is common in floating point arithmethic and the problem is that floating point numbers are discrete in nature, and not continous as real numbers are.
As has been pointed, the <limits.h>
header file has constants to deal with this problem, but you have to use them with care, as they can drive you to new errors:
DBL_EPSILON
is the floating point constant that results of moving from 1.0
to the next number different than 1.0
and has no floating point number between both. In our case it should be, as we are using base 10 numbers and 10 digits, the number should be (1.000000001 - 1.000000000 => 0.00000001
) As the numeration base is different than 10, this number is not a round number, as in base 10.
This number is relative to 1, so if you are substracting two numbers, your epsilon should be relative to the greatest of those numbers
if (fabs(actual - correct) < DBL_EPSILON*fabs(correct)) {
/* consider both results equal */
}
but in real code, as you normally use several sums and substractions, you'll accumulate all those rounding errors, it's easy to get outside of this tolerance, you can use 2.0
or 10.0
times this value.
The floating point numbering system is better described in IEEE-754 specification. You will find there that DBL_EPSILON is not the only constant you have to consider to make two floating points equal (as you approach 0.0
numbers are more together and as you use larger numbers, they are more sparse.)