problemi nel confronto in virgola mobile

void main() { float f = 0.98; if(f <= 0.98) printf("hi"); else printf("hello"); getch(); } 

Sto ottenendo questo problema qui. Usando diversi valori in virgola mobile di am ottenere risultati diversi. Perché questo sta accadendo?

f sta usando la precisione del float , ma 0.98 è in double precisione di default, quindi l’istruzione f <= 0.98 viene confrontata usando la double precisione.

Il f è quindi convertito in un double nel confronto, ma può rendere il risultato leggermente più grande di 0,98.

Uso

 if(f <= 0.98f) 

o usare invece un double per f .


In dettaglio ... supponendo che il float sia IEEE a precisione singola e double è IEEE a doppia precisione .

Questi tipi di numeri in virgola mobile sono memorizzati con la rappresentazione in base 2. Nella base 2 questo numero necessita di una precisione infinita da rappresentare in quanto è un decimale ripetuto:

 0.98 = 0.1111101011100001010001111010111000010100011110101110000101000... 

Un float può solo memorizzare 24 bit di cifre significative, ad es

  0.111110101110000101000111_101... ^ round off here = 0.111110101110000101001000 = 16441672 / 2^24 = 0.98000001907... 

Un double può memorizzare 53 bit di cifre significative, quindi

  0.11111010111000010100011110101110000101000111101011100_00101000... ^ round off here = 0.11111010111000010100011110101110000101000111101011100 = 8827055269646172 / 2^53 = 0.97999999999999998224... 

Quindi lo 0.98 diventerà leggermente più grande in float e più piccolo in double .

È perché i valori in virgola mobile non sono rappresentazioni esatte del numero. Tutti i numeri base dieci devono essere rappresentati sul computer come numeri di base 2. È in questa conversione che si perde la precisione.

Maggiori informazioni su questo a http://en.wikipedia.org/wiki/Floating_point


Un esempio (dall’incontro con questo problema nei miei giorni VB6)

Per convertire il numero 1.1 in un singolo numero a virgola mobile di precisione, è necessario convertirlo in binario. Ci sono 32 bit che devono essere creati.

Il bit 1 è il bit del segno (è negativo [1] o posizione [0]) I bit 2-9 sono per il valore esponente. I bit 10-32 sono per la mantissa (ovvero il significato e il coefficiente di notazione scientifica)

Quindi per 1.1 il singolo valore in virgola mobile viene memorizzato come segue (questo è il valore troncato, il compilatore può arrotondare il bit meno significativo dietro le quinte, ma tutto ciò che faccio è troncarlo, che è leggermente meno preciso ma non cambia i risultati di questo esempio):

 s --exp--- -------mantissa-------- 0 01111111 00011001100110011001100 

Se si nota nella mantissa c’è il pattern di ripetizione 0011. 1/10 in binario è come 1/3 in decimale. Va avanti all’infinito. Quindi, per recuperare i valori dal valore a virgola mobile di precisione a 32 bit, dobbiamo prima convertire l’esponente e la mantissa in numeri decimali in modo che possiamo usarli.

segno = 0 = un numero positivo

esponente: 01111111 = 127

mantissa: 00011001100110011001100 = 838860

Con la mantissa abbiamo bisogno di convertirlo in un valore decimale. Il motivo è che c’è un intero implicito davanti al numero binario (cioè 1.00011001100110011001100). Il numero implicito è perché la mantissa rappresenta un valore normalizzato da utilizzare nella notazione scientifica: 1.0001100110011 …. * 2 ^ (x-127).

Per ottenere il valore decimale di 838860, dividiamo semplicemente per 2 ^ -23 poiché nella mantissa ci sono 23 bit. Questo ci dà 0,099999904632568359375. Aggiungere l’implicito 1 alla mantissa ci dà 1.099999904632568359375. L’esponente è 127 ma la formula richiede 2 ^ (x-127).

Quindi ecco la matematica:

(1 + 099999904632568359375) * 2 ^ (127-127)

1.099999904632568359375 * 1 = 1.099999904632568359375

Come puoi vedere, 1.1 non è realmente memorizzato nel singolo valore in virgola mobile come 1.1.