Archive for maio, 2008

Quando x + 1 == x (ou mais uma razao pra nao usar float/double)

quinta-feira, maio 29th, 2008

O melhor livro técnico que comprei nos últimos tempos foi o Java Puzzlers, que inspira esse post. E digo isso nem tanto pelos corner cases obscuros do Java que você fica conhecendo ao ler o livro (que são divertidíssimos e assustadores ao mesmo tempo) e sim pelos princípios de design que se pode extrair dele. Fortemente recomendado.

Um dos puzzlers do livro mostra um problema com float e double que eu despercebi por todos esses anos, já que não uso esses tipos pra nada. Código como:

float f1 = 16777216f;
float f2 = f1 + 1;
System.out.println(f1 == f2);

imprime true. Sim, true, você não leu errado. O erro na representação da parte fracionária, na verdade, não está limitado a ela; ele atinge a parte inteira e vai ficando mais grave à medida que o número cresce. Com números maiores, você pode somar 50, 100 ou mais e simplesmente ver o valor permanecer o mesmo, porque o tipo de dado não permite representar esses valores.

Eu particularmente achava que o MAX_VALUE e o MIN_VALUE ocorriam justamente antes do erro se manifestar na parte inteira, mas pude comprovar que não. Na verdade, o padrão IEEE 754 dita que isso seja assim, o que significa que toda linguagem que suporta tipos flutuantes segundo esse padrão vai apresentar o mesmo comportamento – que em tese não é um bug, mas uma limitação esperada do design.

Obviamente existem situações específicas em que estes tipos de dados são apropriados e/ou podem-se utilizar técnicas para minimizar ou compensar o erro de representação. Para a maioria das aplicações do mundo, o uso de BigDecimal – ou semelhantes, em outras linguages – é basicamente obrigatório.

Dada a aplicabilidade limitada desses tipos de dados, acho que talvez as linguagens que suportem float e double deveriam requerer que os tipos fossem explicitamente importados, para garantir que o desenvolvedor pelo menos tivesse que fazer esforço pra obter a arma antes de atirar no próprio pé…