Pergunta

Eu tive um pequeno momento WTF esta manhã. Ths WTF pode ser resumida com este:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

A razão parece ser que o x + y expressão é promovido a dobrar e comparada com a versão truncada no z. (Se eu mudar z para double o assert não é acionado).

Eu posso ver que, por razões de precisão, faria sentido para executar todos os aritmética de ponto flutuante de precisão dupla em antes de converter o resultado para precisão simples. Eu encontrei o seguinte parágrafo no padrão (que eu acho que eu meio que já sabia, mas não neste contexto):

4.6.1. "Um rvalue do tipo float pode ser convertido em um rvalue do tipo double. O valor está inalterado"

A minha pergunta é, é x + y garantido para ser promovido para casal ou é a critério do compilador?

UPDATE: Como muitas pessoas afirmou que não se deve usar == de ponto flutuante, eu só queria estado que, no caso específico que eu estou trabalhando com, uma comparação exata é justificada.

Floating comparação ponto é complicado, aqui está um interessante ligação sobre o assunto que eu acho que não foi mencionado.

Foi útil?

Solução

Você não pode geralmente assumem que == funcionará como esperado para os tipos de ponto flutuante. Comparar valores arredondados ou usar construções como abs(a-b) < tolerance vez.

A promoção é inteiramente a critério do compilador (e vai depender de hardware alvo, nível de otimização, etc).

O que está acontecendo neste caso particular é quase certo que os valores são armazenados nos registos FPU em uma precisão maior do que na memória - em geral, obras de hardware FPU modernos com precisão dupla ou superior internos, em qualquer precisão o programador pediu, com o compilador gerar código para fazer as conversões apropriadas quando os valores são armazenados na memória; em uma compilação unoptimised, o resultado de x+y ainda está em um registo no ponto a comparação é feita, mas z terá sido armazenado para a memória e volta obtida, e, assim, truncado para precisão float.

Outras dicas

O projecto de Trabalho para o próximo padrão C ++ 0x seção 5 ponto 11 diz

Os valores dos operandos flutuantes e os resultados das expressões flutuantes podem ser representados com maior precisão e gama do que a exigida pelo tipo; os tipos não são alterados, assim

Assim, a critério do compilador.

Usando gcc 4.3.2, a afirmação é não acionado e, de fato, o rvalue retornou de x + y é um float, em vez de um double.

Portanto, cabe ao compilador. É por isso que nunca é aconselhável confiar em igualdade exata entre dois valores de ponto flutuante.

A C ++ FAQ Lite tem alguma discussão mais aprofundada sobre o tema:

É o problema, já que o número float para conversão de binário não dá precisão preciso.

E dentro sizeof(float) bytes ele não pode acomodar valor preciso do número de bóia e operação aritmética pode levar a aproximação e, portanto, a igualdade falha.

Veja abaixo exemplo.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert

Eu acho que seria a critério do compilador, mas você sempre pode forçá-lo com um elenco se isso era o seu pensamento?

No entanto, outra razão para carros alegóricos Nunca compare diretamente.

if (fabs(result - expectedResult) < 0.00001)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top