Peña wrote:
–snip–
Unfortunately, there is no easy solution to this problem. Here is a
catalog of often proposed solutions and why they do not work:
1 (proposed by doug meyer in this thread) Always use
(x-y).abs < Float::EPSILON
as a test for equality.
This won’t work because the rounding error easily can get bigger than
Float::EPSILON, especially when dealing with numbers that are bigger
than unity. e.g.
y = 100.1 + 0.3
y - 100.4 # => -1.421e-14, while Float::EPSILON = 2.22e-16
2 Always use
(x-y).abs < (x.abs + y.abs) * Float::EPSILON)
as a test for equality.
Better than the first proposal, but won’t work if the rounding error
gets too large after a complex computation.
In addition, (1) and (2) suffer from the problem that x==y and y==z do
not imply x==z.
3 Use Bigdezimal
This only shifts the problem a few decimal places down, and tests for
equality will fail as with the normal floats.
4 Use Rationals
Works if you only have to deal with rational operations. But doesn’t
solve the following
x = sqrt(2)
y = x + 1
x + 0.2 == y - 0.8 # => false
In addition, rational arithmetic can produce huge numbers pretty fast,
and this will slow down computations enormously.
5 Use a symbolic math package
This could in theory solve the issue with equality, but in practice
there
is no way to decide that two symbolic representations of a number are
the
same, like
1 / (sqrt(2) - 1) == sqrt(2) + 1
Also, very, very slow.
6 Use interval arithmetic
Gives you strict bounds on your solution, but can’t answer x==y.
Summing up, when using floating point arithmetic there is no one true
way.
There is no substitute for understanding numbers and analyzing your
problem.
HTH,
Michael