não se testa igualdade de números de ponto flutuante

Me bati com a seguinte falha (usando testes unitários no Rails):

  1) Failure:
test_stock(ProductTest) [test/unit/product_test.rb:84]:
<109.45> expected but was
<109.45>.

Daí eu vi no Ruby Weekly News que não é uma boa idéia testar igualdade de números de ponto flutuante, em qualquer linguagem. A notícia apontava também para o clássico What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Resolvi testar isso em várias linguagens pra fixar na minha cabeça. Aqui os resultados:

Ruby:

$ irb
irb(main):001:0> (80.12 + 19.45)
=> 99.57
irb(main):002:0> (80.12 + 19.45) == 99.57
=> false

Python foi um pouco melhor, não mostrando o resultado da soma arredondado, deixando mais clara a diferença.

$ python
Python 2.4.4 (#2, Oct 20 2006, 00:23:25)
[GCC 4.1.2 20061015 (prerelease) (Debian 4.1.1-16.1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> (80.12 + 19.45)
99.570000000000007
>>> (80.12 + 19.45) == 99.57
False

Perl:

$ iperl
iperl> (80.12 + 19.45)
=> '99.57'
iperl> (80.12 + 19.45) == 99.57
=> ''

notas:

  1. iperl é um console Perl interativo que estou desenvolvendo, a ser lançado em breve (avisarei aqui).
  2. Em Perl, 0 (zero) e '' (string vazia) representam a noção de falso.

C:

$ cat teste.c
#include <stdio.h>

int main() {
  printf("%d\n", (80.12 + 19.45) == 99.57);
  printf("%d\n", 99.57 == 99.57);
  return 0;
}
$ make teste
cc     teste.c   -o teste
$ ./teste
0
1

Pronto, me eduquei. Lembre-se, sempre: não se testa igualdade de números de ponto flutuante. De novo, repita comigo: não se testa igualdade de números de ponto flutuante.

Sim, a solução para o problema foi: ao invés de testar igualdade, testar delta entre os valores. Ao invés de assert_equal x, y, usar assert_in_delta x, y, 0.001