Extração estranha de cadeias numéricas em PHP

Estou juntando duas cordas numéricas $ a e $ b e depois comparando o resultado com outra cadeia numérica $ c. Todos os três números são armazenados como strings e sendo convertidos em flutuadores por PHP na etapa de comparação.

Por algum motivo, o teste $ a + $ b == $ c não avalia como verdadeiro, mesmo assim.

Você pode recriar o problema com este script:

 

Estranhamente, se eu mudar os valores um pouco para que $ a = -111.11, $ b = -22.23 e $ c = -133.34 funcione conforme o esperado.

Estou perdendo algo óbvio, ou isso é um bug com o PHP?

Você está executando uma limitação da aritmética de ponto flutuante. Assim como existem certos números, você não pode representar exatamente em decimal (1/3 por exemplo), então há certos números que você não pode representar exatamente em binário de ponto flutuante.

Você nunca deve tentar comparar os números de ponto flutuante para a igualdade, pois as limitações do ponto flutuante tornam improvável que as variables ​​que você está comparando tenham um valor real que corresponda exatamente ao valor que você acha que eles têm. Você precisa adicionar um “fudge factor”, ou seja, se os dois números são semelhantes a uma certa tolerância, então você deve considerar que eles são iguais.

Você pode fazer isso subtraindo um número de outro e vendo se o resultado absoluto está abaixo do seu limite (no meu exemplo, 0.01):

 if (abs ($someFloatingPointNumber - $someOtherFloatingPointNumber) < = 0.01) { // The values are close enough to be considered equal } 

Claro, isso combinado com erros de arredondamento que podem se encheckboxr com sucessivas operações matemáticas significam que os números de ponto flutuante muitas vezes não são a melhor escolha de qualquer maneira, e devem ser evitados sempre que possível. Por exemplo, se você estiver lidando com moeda, armazene seus valores como números inteiros na unidade menor (centavos por GBP, centavos por USD, etc.) e somente converte para a unidade principal dividindo-se por 100 para exibição.

Da grande checkbox vermelha nesta página: http://php.net/manual/en/language.types.float.php

nunca compare os números de ponto flutuante para a igualdade.

Basicamente, você não está obtendo os números corretos, porque eles são salvos em um formato ligeiramente diferente, então, quando você compara, fica ferrado.

Esse link de @Corbin é realmente bom, então eu estou adicionando isso apenas pelo amor 🙂
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

O que todo cientista de computadores deve saber sobre a aritmética de ponto flutuante

Este artigo apresenta um tutorial sobre os aspectos de ponto flutuante que têm um impacto direto nos designers de sistemas informáticos. Ele começa com o plano de fundo na representação de ponto flutuante e erro de arredondamento, continua com uma discussão do padrão de ponto flutuante IEEE e conclui com inúmeros exemplos de como os construtores de computadores podem suportar melhor o ponto flutuante.

Seu número tem duas posições decimais?

Se assim for, você pode tentar isso:

 $aDec = round($a * 100); $bDec = round($b * 100); $cDec = round($c * 100); if ($aDec + $bDec == $cDec) { ... }