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:
-
iperl
é um console Perl interativo que estou desenvolvendo, a ser lançado em breve (avisarei aqui). - 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