Comparação de ponto flutuante `a! = 0.7`

Duplicar Possível:
problemas na comparação de ponto flutuante

#include  #include  main() { float a = 0.7; if(a < 0.7) printf("C"); else printf("C++"); } 

No código acima, a saída é C Eu tentei esse código em Code :: Blocks e Pelles C, mas recebi a mesma resposta. Eu gostaria de saber o motivo disso em detalhes!

Em binário, 0,7 é:

 b0.1011001100110011001100110011001100110011001100110011001100110... 

No entanto, 0.7 é um literal de precisão dupla, cujo valor é 0.7 arredondado para o valor de precisão dupla representável mais próximo, que é:

 b0.10110011001100110011001100110011001100110011001100110 

Em decimal, é exatamente isso:

  0.6999999999999999555910790149937383830547332763671875 

Quando você escreve float a = 0.7 , esse valor double é arredondado novamente para precisão simples, e a obtém o valor binário:

 b0.101100110011001100110011 

o que é exatamente

  0.699999988079071044921875 

em decimal.

Quando você faz a comparação (a < 0.7) , você está comparando esse valor de precisão simples (convertido em duplo, que não arredonda, porque todos os valores de precisão única são representáveis ​​em precisão dupla) com o valor de precisão dupla original. Porque

  0.699999988079071044921875 < 0.6999999999999999555910790149937383830547332763671875 

a comparação retorna corretamente true e seu programa imprime "C" .

Por favor note que nada disso é diferente em C ++, aparências do código em questão em contrário. Há certas otimizações de compilador (numericamente inseguras) que podem alterar o comportamento, mas elas não são exclusivas de C ou C ++.

É porque 0.7 tem o tipo double , portanto, a é convertido em double e a comparação é feita nesse tipo. Como 0.7 não é representável exatamente no ponto flutuante binário, você obtém algum erro de arredondamento e a comparação se torna verdadeira.

Você pode:

 if( a < 0.7f ) { .... 

Mas, na verdade, esse efeito também é verdadeiro para o C ++, então sua condicional não é exatamente correta.

Bart deu uma referência muito boa em seu comentário, mas eu também recomendaria essa regra muito simples:

Nem todos os números podem ser representados exatamente no computador, portanto, assim que você usar números de ponto flutuante (como float e double), você deve esperar que possa haver um erro pequeno e imprevisível em cada número armazenado. Não, não é realmente random ou imprevisível, mas até que você saiba mais sobre isso, você pode considerá-lo imprevisível. Portanto, um copmparison como seu a <0.7 pode se tornar verdadeiro, e pode não ser verdade. Não escreva o código que depende dele.