uscita strano in confronto del galleggiante con galleggiante letterale
-
12-09-2019 - |
Domanda
float f = 0.7;
if( f == 0.7 )
printf("equal");
else
printf("not equal");
Perché il not equal
uscita?
Perché accade questo?
Soluzione
Questo accade perché nella sua dichiarazione
if(f == 0.7)
il 0,7 è considerato un doppio. Prova 0.7f per garantire il valore viene trattato come un float:
if(f == 0.7f)
Ma, come Michael suggerito nei commenti qui sotto non si dovrebbe mai verificare l'uguaglianza esatta dei valori in virgola mobile.
Altri suggerimenti
Questa risposta per completare quelli esistenti: si noti che 0.7 non è rappresentabile esattamente sia come un galleggiante (o come un doppio). Se è stata rappresentata esattamente, allora non ci sarebbe alcuna perdita di informazioni durante la conversione di galleggiare e poi di nuovo al doppio, e non avrebbe questo problema.
Si potrebbe anche sostenere che ci dovrebbe essere un avviso del compilatore per le costanti in virgola mobile letterali che non può essere rappresentato esattamente, in particolare quando lo standard è così confusa per quanto riguarda se l'arrotondamento sarà effettuato in fase di esecuzione in modalità che ha stato impostato come quel tempo o al momento della compilazione in un'altra modalità di arrotondamento.
Tutti i numeri non interi che possono essere rappresentati esattamente avere 5
come la loro ultima cifra decimale. Purtroppo, il contrario non è vero: alcuni numeri sono 5
come la loro ultima cifra decimale e non può essere rappresentato esattamente. I piccoli numeri interi possono essere rappresentati esattamente, e la divisione per una potenza di 2 trasforma un numero che può essere rappresentato in un altro che può essere rappresentato, fino a quando non si immette nel regno dei numeri denormalizzati.
Prima di tutto lascia guardare dentro il numero float. Prendo 0.1f è 4 byte di lunghezza (binary32), in esadecimale è
3D CC CC CD .
Entro la standart IEEE 754 per convertirlo in decimale che dobbiamo fare in questo modo:
In 3D binario CC CC CD è
0 01111011 1001100 11001100 11001101
qui prima cifra è un po 'segno. 0 mezzi (-1) ^ 0 che il nostro numero è positivo.
Secondi 8 bit è un esponente. In binario è 01.111.011 - in decimale 123. Ma il vero esponente è 123-127 (sempre 127) = -4 , è media abbiamo bisogno di moltiplicare il numero avremo per 2 ^ (- 4 ).
Gli ultimi 23 byte è la precisione significando. C'è il primo bit moltiplichiamo per 1 / (2 ^ 1) (0,5), secondo per 1 / (2 ^ 2) (0,25) e così via. Ecco quello che otteniamo:
Abbiamo bisogno di aggiungere tutti i numeri (potenza di 2) e aggiungere ad essa 1 (sempre 1, da standart). È
1,60000002384185791015625
Ora cerchiamo di moltiplicare questo numero per 2 ^ (- 4), è da Exponent. Abbiamo il numero appena sopra devide da 2 a quattro tempi:
0,100000001490116119384765625
Ho usato MS Calculator
**
Ora la seconda parte. Conversione da decimale a binario.
**
Prendo il numero 0.1
E proprio agio, perché non v'è alcuna parte intera. Primo bit di segno - è 0.
Esponente e significando la precisione io calcolare ora. La logica è moltiplicare per 2 numero intero (0,1 * 2 = 0.2) e se è superiore a 1 sottrarre e continuare.
E il numero è 0,00011001100110011001100110011, standart dice che dobbiamo spostamento a sinistra prima di arrivare 1. (qualcosa). Come vedete abbiamo bisogno di 4 turni, da questo numero calcolo esponente (127-4 = 123 ). E la precisione significando ora è
10011001100110011001100 (e ci si perde bit).
Ora il numero intero. bit di segno 0 esponente è 123 ( 01.111.011 ) e significando la precisione è 10011001100110011001100 e complesso è
00111101110011001100110011001100
confrontiamo con quelli che abbiamo da precedente capitolo
00111101110011001100110011001101
Come si vede la dura bit non sono uguali. E 'perché ho troncare il numero. La CPU e compilatore sa che il è qualcosa dopo significando precisione non può tenere e basta impostare l'ultimo bit a 1.
Il problema si sta affrontando è, come altri commentatori hanno notato, che è generalmente pericoloso per verificare l'equivalenza esatta tra carri allegorici, come errori di inizializzazione, o errori di arrotondamento nei calcoli può introdurre piccole differenze che causeranno l'operatore == per return false.
Una pratica migliore è quello di fare qualcosa di simile
float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
printf("equal");
else
printf("not equal");
Supponendo che FLT_EPSILON è stato definito come un livello sufficientemente piccolo valore float per la piattaforma.
Dal momento che gli errori di arrotondamento o di inizializzazione saranno difficilmente superano il valore di FLT_EPSILON, questo vi darà il test di equivalenza affidabile che stai cercando.
Un sacco di risposte giro per il web fanno l'errore di guardare la differenza abosulute tra i numeri in virgola mobile, questo è valido solo per casi particolari, il modo robusto è quello di guardare la differenza relativa, come in seguito:
// 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;
}
Un altro interrogativo accanto esatto era legato a questa quindi la risposta anni di ritardo. Non penso che le risposte di cui sopra sono completi.
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
Perché ha fun3 e fun4 restituire uno e non gli altri? perché funziona fun5?
Si tratta del linguaggio. La lingua dice che 0.7 è un doppio se non si utilizza questa sintassi 0.7f, allora è una sola. Quindi
float x=0.7;
doppio 0.7 viene convertito in un singolo e memorizzato in x.
if(x==0.7) return(1);
La lingua dice che dobbiamo promuovere la maggiore precisione in modo che il singolo di x viene convertito in un letto matrimoniale e confronto con la doppia 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
singolo 3f333333 doppia 3fe6666666666666
Come Alexandr ha sottolineato che se la risposta rimane IEEE 754 è un singolo
seeeeeeeefffffffffffffffffffffff
E doppia è
seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
con 52 bit di frazione piuttosto che il 23 quell'unico trovi.
00111111001100110011... single
001111111110011001100110... double
0 01111110 01100110011... single
0 01111111110 01100110011... double
Proprio come 1/3 ° in base 10 è ,3333,333 mila ... per sempre. Abbiamo un pattern ripetuto qui 0110
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
E qui è la risposta.
if(x==0.7) return(1);
x contiene 01100110011001100110011 come sua frazione, quando questo viene convertito indietro per raddoppiare la frazione è
01100110011001100110011000000000....
, che non è uguale a
01100110011001100110011001100110...
ma qui
if(x==0.7f) return(1);
che la promozione non accade gli stessi modelli di bit sono confrontati tra loro.
Perché il 1,0 lavoro?
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...
In entrambi i casi la frazione è tutti zeri. Quindi, la conversione da doppia a singola a doppia v'è alcuna perdita di precisione. Converte da singolo a doppio esattamente e il confronto bit dei due valori funziona.
Il più alto votato e controllato dalla risposta Halfdan è la risposta corretta, questo è un caso di precisione mista e non si dovrebbe mai fare di uguale confronto.
L'intrattenimento non era il motivo per cui indicato in quella risposta. 0.7 fallisce 1.0 opere. Perché non riescono 0.7 non era indicata. Una domanda duplicato 1.1 non riesce pure.
Modifica
I uguali possono essere prese fuori il problema qui, è una questione diversa che è già stato risposto, ma è lo stesso problema e ha anche la "ciò che il ..." shock iniziale.
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
Perché uno spettacolo come inferiore e l'altro non inferiore a? Quando dovrebbero essere uguali.
Dall'alto sappiamo la storia 0.7.
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
01100110011001100110011000000000....
è inferiore.
01100110011001100110011001100110...
0.6 è un diverso modello di ripetizione 0011 anziché 0110.
ma quando convertito da un letto ad una sola o in generale quando rappresentata come un singolo IEEE 754.
00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single
IEEE 754 utilizza modalità di arrotondamento, arrotondare, arrotondare o rotonda a zero. I compilatori tendono per arrotondare per difetto. Se vi ricordate l'arrotondamento in grado di scuola 12345678 se volevo arrotondare alla 3a cifra dall'alto sarebbe 12300000, ma intorno alla cifra successiva 1.235.000 se la cifra dopo è 5 o maggiore di arrotondare. 5 è 1/2 del 10 della base (decimale) in binario 1 è 1/2 della base, quindi se la cifra dopo la posizione che vogliamo round è 1 poi arrotondare il resto Dont. Così, per 0,7 non abbiamo arrotondare, per 0,6 facciamo arrotondare.
E ora è facile vedere che
00110011001100110011010
convertito in un letto a causa di (x <0,7)
00110011001100110011010000000000....
è maggiore di
00110011001100110011001100110011....
Quindi, senza dover parlare con uguale il problema si presenta ancora 0.7 è doppia 0.7f è single, l'operazione viene promosso la massima precisione se differiscono.
Considerate questo:
int main()
{
float a = 0.7;
if(0.7 > a)
printf("Hi\n");
else
printf("Hello\n");
return 0;
}
se (0,7> a) qui è una variabile float e 0.7
è un doppio costante. Il doppio 0.7
costante è maggiore della variabile float a. Da qui la se la condizione è soddisfatta e la stampa 'Hi'
Esempio:
int main()
{
float a=0.7;
printf("%.10f %.10f\n",0.7, a);
return 0;
}
Output:
0,7 miliardi ,6999,999881 millions
Se si cambia tipo di dati f a doppia , che sarà stampare uguale , questo è perché le costanti in virgola mobile memorizzati in doppia e non galleggianti in lungo , doppia precisione è alta e galleggiante è meno precisa, doppio valore memorizzato in 64 bit valore binario e float memorizzati in 32 bit binari, sarà completamente chiaro se si vede il metodo di galleggiare conversione numeri in virgola alla conversione binaria.