Além Float promovido a dupla?
-
12-09-2019 - |
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.
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)