saída estranha na comparação do flutuador com literal flutuador
-
12-09-2019 - |
Pergunta
float f = 0.7;
if( f == 0.7 )
printf("equal");
else
printf("not equal");
Por que o not equal
saída?
Por que isso acontece?
Solução
Isso acontece porque, em sua declaração
if(f == 0.7)
a 0,7 é tratado como uma dupla. Tente 0.7f para garantir o valor é tratado como um float:
if(f == 0.7f)
Mas, como Michael sugeriu nos comentários abaixo você nunca deve teste de igualdade exata de valores de ponto flutuante.
Outras dicas
Esta resposta para complementar os já existentes: nota que 0,7 não é representável exatamente quer como um float (ou como um duplo). Se ele foi representado exatamente, então não haveria perda de informação ao converter a flutuar e depois voltar para o dobro, e você não teria esse problema.
Pode até ser argumentado que deveria haver um aviso do compilador para constantes de ponto flutuante literais que não podem ser representados exatamente, especialmente quando o padrão é tão confuso sobre se o arredondamento será feito em tempo de execução no modo que tem foi definido como o tempo ou em tempo de compilação em outro modo de arredondamento.
Todos os números não-inteiros que podem ser representados têm exatamente 5
como seu último dígito decimal. Infelizmente, o inverso não é verdadeiro: alguns números têm 5
como seu último dígito decimal e não pode ser representado exatamente. Pequenos números inteiros podem ser representados exatamente, e divisão por uma potência de 2 Transforma um número que pode ser representado em outro que pode ser representada, desde que você não entrar no reino de números desnormalizados.
Em primeiro lugar número dentro de olhar flutuador let. Eu tomo 0.1f é de 4 bytes de comprimento (binary32), em hexadecimal é
CD CC CC 3D .
pelo Standart IEEE 754 para convertê-lo para decimal devemos fazer assim:
Em binário 3D CC CC CD é
0 01111011 1001100 11001100 11001101
aqui primeiro dígito é um bit de sinal. 0 meio (-1) ^ 0 que o nosso número é positivo.
Segundo 8 bits é um expoente. Em binário é 01111011 - em decimal 123. Mas o verdadeiro expoente é 123-127 (sempre 127) = -4 , é necessário que queremos dizer para multiplicar o número que terá por 2 ^ (- 4 ).
Os últimos 23 bytes é a precisão significando. Há o primeiro bit que multiplicar por 1 / (2 ^ 1) (0,5), segundo por 1 / (2 ^ 2) (0,25) e assim por diante. Aqui o que temos:
Precisamos adicionar todos os números (potência de 2) e adicionar-lhe 1 (sempre 1, por Standart). É
1,60000002384185791015625
Agora vamos multiplicar esse número por 2 ^ (- 4), é de Exponent. Nós número apenas Devide acima por 2 quatro vezes:
0,100000001490116119384765625
I utilizado MS calculadora
**
Agora a segunda parte. Convertendo de decimal para binário.
**
eu tomo o número de 0,1
Ele facilidade porque não há nenhuma parte inteira. Primeiro bit de sinal - é 0.
precisão e expoente significando que irá calcular agora. A lógica é multiplicar por dois o número inteiro (0,1 * 2 = 0,2) e se for maior do que um substrato e continuar.
E o número é ,00011001100110011001100110011, Standart diz que devemos desvio para a esquerda antes de chegarmos 1. (algo). Como você vê que precisamos 4 turnos, a partir deste número cálculo Exponent (127-4 = 123 ). Ea precisão significando agora é
10011001100110011001100 (e não é perdido bits).
Agora o número inteiro. Bit de sinal 0 Exponent é 123 ( 01111011 ) e precisão significand é 10011001100110011001100 e toda é
00111101110011001100110011001100
Vamos compará-lo com aqueles que têm a partir do capítulo anterior
00111101110011001100110011001101
Como você vê a dura pouco não são iguais. É porque eu truncar o número. A CPU e compilador sabe que o é algo depois precisão significando não pode segurar e apenas definir o último bit a 1.
O problema que você está enfrentando é, como outros comentadores notaram, que geralmente é inseguro para teste de equivalência exata entre carros alegóricos, como erros de inicialização, ou erros de arredondamento nos cálculos podem apresentar pequenas diferenças que fará com que o operador == para return false.
A melhor prática é fazer algo como
float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
printf("equal");
else
printf("not equal");
Assumindo que FLT_EPSILON tem sido definida como um valor float adequadamente pequena para sua plataforma.
Uma vez que o arredondamento ou erros de inicialização será pouco provável que exceder o valor das FLT_EPSILON, isso vai lhe dar o teste de equivalência de confiança que você está procurando.
Um monte de respostas em torno da web cometem o erro de olhar para a diferença abosulute entre os números de ponto flutuante, isto é válido apenas para casos especiais, a forma robusta é olhar para a diferença relativa como na abaixo:
// Floating point comparison:
bool CheckFP32Equal(float referenceValue, float value)
{
const float fp32_epsilon = float(1E-7);
float abs_diff = std::abs(referenceValue - value);
// Both identical zero is a special case
if( referenceValue==0.0f && value == 0.0f)
return true;
float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) );
if(rel_diff < fp32_epsilon)
return true;
else
return false;
}
Outra pergunta exata perto estava ligado a este, portanto, o ano resposta tardia. Eu não acho que as respostas acima estão completas.
int fun1 ( void )
{
float x=0.7;
if(x==0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=1.1;
if(x==1.1) return(1);
else return(0);
}
int fun3 ( void )
{
float x=1.0;
if(x==1.0) return(1);
else return(0);
}
int fun4 ( void )
{
float x=0.0;
if(x==0.0) return(1);
else return(0);
}
int fun5 ( void )
{
float x=0.7;
if(x==0.7f) return(1);
else return(0);
}
float fun10 ( void )
{
return(0.7);
}
double fun11 ( void )
{
return(0.7);
}
float fun12 ( void )
{
return(1.0);
}
double fun13 ( void )
{
return(1.0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00000 mov r0, #0
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
00000010 <fun3>:
10: e3a00001 mov r0, #1
14: e12fff1e bx lr
00000018 <fun4>:
18: e3a00001 mov r0, #1
1c: e12fff1e bx lr
00000020 <fun5>:
20: e3a00001 mov r0, #1
24: e12fff1e bx lr
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
Por que fun3 e um fun4 retorno e não os outros? porque funciona fun5?
É sobre a língua. A linguagem diz que 0,7 é um duplo a menos que você use este 0.7f sintaxe, então é um único. Então
float x=0.7;
o dobro 0,7 é convertido a um único e armazenado em x.
if(x==0.7) return(1);
A linguagem diz que temos de promover a maior precisão de modo que o único em x é convertido para um casal e comparados com a dupla 0.7.
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
3f333333 única double 3fe6666666666666
Como Alexandr apontou, se essa resposta permanece IEEE 754 um único é
seeeeeeeefffffffffffffffffffffff
E duplo é
seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
com 52 bits de fracção em vez de a 23 que tem único.
00111111001100110011... single
001111111110011001100110... double
0 01111110 01100110011... single
0 01111111110 01100110011... double
Assim como 1/3 na base 10 é 0,3333333 ... para sempre. Temos um padrão de repetição aqui 0110
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
E aqui está a resposta.
if(x==0.7) return(1);
x contém 01100110011001100110011 como sua fração, quando isso fica para trás convertido para dobrar a fração for
01100110011001100110011000000000....
que não é igual a
01100110011001100110011001100110...
mas aqui
if(x==0.7f) return(1);
acontecer que does not promoção os mesmos padrões de bits são comparados uns com os outros.
Por que 1.0 trabalho?
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
0011111110000000...
0011111111110000000...
0 01111111 0000000...
0 01111111111 0000000...
Em ambos os casos a fração for todos os zeros. Então conversão de dupla para simples para duplo não há nenhuma perda de precisão. Ele converte de simples para duplo exatamente ea comparação dos dois valores obras bit.
O mais votado e verificado resposta por Halfdan é a resposta correta, este é um caso de precisão misturados e você nunca deve fazer um igual comparação.
O porquê não era mostrado nessa resposta. 0,7 falhar 1.0 funciona. Por que 0,7 falhar não era mostrado. Uma questão duplicado 1,1 falhar bem.
Editar
Os iguais podem ser retirados do problema aqui, é uma questão diferente, que já foi respondida, mas é o mesmo problema e também tem o "o que o ..." choque inicial.
int fun1 ( void )
{
float x=0.7;
if(x<0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=0.6;
if(x<0.6) return(1);
else return(0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00001 mov r0, #1
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
Por que um show como menos do que eo outro não menos do que? Quando eles devem ser iguais.
De cima nós conhecemos a história 0,7.
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
01100110011001100110011000000000....
é inferior a.
01100110011001100110011001100110...
0.6 é um teste padrão de repetição diferentes 0011 em vez de 0110.
mas quando convertido a partir de um duplo a um único ou em geral quando representada como um único IEEE 754.
00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single
IEEE 754 usa o arredondamento modos, até rodada, para baixo redonda ou redondas para zero. Compiladores tendem a arredondar para cima por padrão. Se você se lembrar de arredondamento na escola 12345678 se eu queria volta para o terceiro dígito do topo seria 12.300.000 mas volta para o próximo dígito 1.235.000 se o dígito depois é 5 ou superior, em seguida, até round. 5 é 1/2 de 10 de base (Decimal) em binário 1 é 1/2 da base por isso, se o dígito depois da posição que queremos rodada é de 1 em seguida, volta-se outra coisa não faça. Assim, para 0,7 não tivemos até rodada, para 0,6 fazemos até round.
E agora, é fácil ver que
00110011001100110011010
convertido para um duplo devido a (x <0,7)
00110011001100110011010000000000....
é maior do que
00110011001100110011001100110011....
Assim, sem ter que falar sobre o uso iguala o problema ainda se apresenta 0,7 é o dobro 0.7f é único, a operação é promovido para a mais alta precisão se forem diferentes.
Considere o seguinte:
int main()
{
float a = 0.7;
if(0.7 > a)
printf("Hi\n");
else
printf("Hello\n");
return 0;
}
se 0.7
é uma constante dupla. A constante 0.7
dupla é maior do que a variável flutuador um. Daí a se a condição for satisfeita e imprime 'Hi'
int main()
{
float a=0.7;
printf("%.10f %.10f\n",0.7, a);
return 0;
}
Output:
0,7000000000 0,6999999881
Se você alterar tipo de dados f para duplo , ele vai imprimir igualar , Isto porque constantes em ponto flutuante armazenado em duplo e não flutuando no longo , dupla precisão é alta e flutuador tem menos preciso, duplo valor armazenado em /> armazenado em 32 binário bit, vai ser completamente claro, se você ver o método de flutuar conversão números de ponto à conversão binário.