Doppia precisione: posizioni decimali

Da quello che ho letto, un valore del tipo di dati double ha una precisione approssimativa di 15 cifre decimali. Tuttavia, quando uso un numero la cui rappresentazione decimale si ripete, come 1.0 / 7.0, trovo che la variabile contenga il valore di 0.14285714285714285 – che è 17 posizioni (tramite il debugger).

Mi piacerebbe sapere perché è rappresentato come 17 posti internamente, e perché una precisione di 15 è sempre scritta a ~ 15?

Un doppio IEEE ha 53 bit significativi (questo è il valore di DBL_MANT_DIG in ). Sono circa 15,95 cifre decimali (log10 (2 53 )); l’implementazione imposta DBL_DIG su 15, non su 16, perché deve arrotondare per DBL_DIG . Quindi hai quasi una cifra decimale in più di precisione (oltre a ciò che implica DBL_DIG==15 ) per questo motivo.

La funzione nextafter() calcola il numero rappresentabile più vicino a un numero dato; può essere usato per mostrare quanto sia preciso un dato numero.

Questo programma:

 #include  #include  #include  int main() { double x = 1.0/7.0; printf("FLT_RADIX = %d\n", FLT_RADIX); printf("DBL_DIG = %d\n", DBL_DIG); printf("DBL_MANT_DIG = %d\n", DBL_MANT_DIG); printf("%.17g\n%.17g\n%.17g\n", nextafter(x, 0.0), x, nextafter(x, 1.0)); } 

mi dà questo risultato sul mio sistema:

 FLT_RADIX = 2 DBL_DIG = 15 DBL_MANT_DIG = 53 0.14285714285714282 0.14285714285714285 0.14285714285714288 

(Puoi sostituire %.17g , per esempio, %.64g per vedere più cifre, nessuna delle quali è significativa.)

Come puoi vedere, l’ultima cifra decimale visualizzata cambia per 3 con ogni valore consecutivo. Il fatto che l’ultima cifra visualizzata di 1.0/7.0 ( 5 ) corrisponda al valore matematico è in gran parte casuale; è stata una supposizione fortunata. E la cifra arrotondata corretta è 6 , non 5 . Sostituendo 1.0/7.0 da 1.0/3.0 ottiene questo risultato:

 FLT_RADIX = 2 DBL_DIG = 15 DBL_MANT_DIG = 53 0.33333333333333326 0.33333333333333331 0.33333333333333337 

che mostra circa 16 cifre decimali di precisione, come ci si aspetterebbe.

In realtà sono 53 posizioni binarie, che si traducono in 15 posizioni decimali stabili, il che significa che se inizi un round con un numero di 15 cifre decimali, convertilo in un double , e poi intorno al double in 15 posizioni decimali ottieni lo stesso numero Per rappresentare in modo univoco un double hai bisogno di 17 cifre decimali (nel senso che per ogni numero con 17 cifre decimali esiste un double più vicino) ed è per questo che vengono mostrati 17 luoghi, ma non tutti i numeri decimali vengono mappati a valori double diversi (come negli esempi nelle altre risposte).

La rappresentazione decimale dei numeri in virgola mobile è piuttosto strana. Se hai un numero di 15 cifre decimali e lo converti in un double , quindi stampalo con esattamente 15 posizioni decimali, dovresti ottenere lo stesso numero. D’altra parte, se si stampa una double arbitraria con 15 cifre decimali e la si converte in una double , non si otterrà necessariamente lo stesso valore, per cui occorrono 17 cifre decimali. E né 15 né 17 cifre decimali sono sufficienti per visualizzare con precisione l’equivalente decimale esatto di un double arbitrario. In generale, hai bisogno di oltre 100 posizioni decimali per farlo esattamente.

Vedi la pagina di Wikipedia per la precisione doppia e questo articolo sulla precisione in virgola mobile .

Un doppio contiene 53 cifre binarie in modo accurato, che è ~ 15.9545898 cifre decimali. Il debugger può mostrare tutte le cifre che desidera per essere più precisi al valore binario . Oppure potrebbe richiedere meno cifre e binari, ad esempio 0,1 prende 1 cifra in base 10, ma infinito in base 2.

Questo è strano, quindi mostrerò un esempio estremo. Se realizziamo un valore in virgola mobile super semplice che contiene solo 3 cifre binarie di accuratezza e nessuna mantissa o segno (quindi range è 0-0.875), le nostre opzioni sono:

 binary - decimal 000 - 0.000 001 - 0.125 010 - 0.250 011 - 0.375 100 - 0.500 101 - 0.625 110 - 0.750 111 - 0.875 

Ma se si fanno i numeri, questo formato è preciso solo a cifre decimali 0,903089987. Nemmeno 1 cifra è accurata. Come è facile vedere, dal momento che non c’è alcun valore che inizia con 0.4??0.9?? e tuttavia per visualizzare la precisione completa, sono necessarie 3 cifre decimali.

tl; dr: il debugger mostra il valore della variabile in virgola mobile con una precisione arbitraria (19 cifre nel tuo caso), che non è necessariamente correlato con la precisione del formato in virgola mobile (17 cifre nel tuo caso).

IEEE 754 virgola mobile è fatto in binario. Non esiste una conversione esatta da un determinato numero di bit a un determinato numero di cifre decimali. 3 bit possono contenere valori da 0 a 7 e 4 bit possono contenere valori da 0 a 15. Un valore compreso tra 0 e 9 richiede circa 3,5 bit, ma non è esatto neanche uno.

Un numero di precisione doppia IEEE 754 occupa 64 bit. Di questo, 52 bit sono dedicati al significato (il resto è un bit ed esponente di segno). Dato che il significato e (di solito) normalizzato, c’è un bit implicito del 53 ° .

Ora, dati 53 bit e circa 3,5 bit per cifra, la divisione semplice ci fornisce 15.1429 cifre di precisione. Ma ricorda, quel 3,5 bit per cifra decimale è solo un’approssimazione, non una risposta perfettamente accurata.

Molti (più?) Debugger guardano effettivamente il contenuto dell’intero registro. Su un x86, questo è in realtà un numero di 80 bit. L’unità in virgola mobile x86 verrà normalmente regolata per eseguire calcoli con precisione a 64 bit, ma internamente utilizza effettivamente un paio di “bit di guardia”, che fondamentalmente significa internamente che esegue il calcolo con alcuni bit di precisione in più così può arrotondare correttamente l’ultimo. Quando il debugger controlla l’intero registro, di solito trova almeno una cifra in più che è ragionevolmente precisa, anche se poiché quella cifra non avrà alcun bit di guardia, potrebbe non essere arrotondata correttamente.

È perché viene convertito da una rappresentazione binaria. Solo perché ha stampato tutte quelle cifre decimali non significa che possa rappresentare tutti i valori decimali con quella precisione. Prendi, per esempio, questo in Python:

 >>> 0.14285714285714285 0.14285714285714285 >>> 0.14285714285714286 0.14285714285714285 

Nota come ho cambiato l’ultima cifra, ma ha stampato lo stesso numero comunque.

Nella maggior parte dei contesti in cui vengono utilizzati i double valori, i calcoli avranno una certa quantità di incertezza. La differenza tra 1.33333333333333300 e 1.33333333333333399 potrebbe essere inferiore alla quantità di incertezza esistente nei calcoli. La visualizzazione del valore di “2/3 + 2/3” come “1.33333333333333” può essere più significativa della visualizzazione come “1.33333333333333319”, poiché quest’ultima visualizzazione implica un livello di precisione che in realtà non esiste.

Nel debugger, tuttavia, è importante indicare in modo univoco il valore contenuto da una variabile, inclusi i bit di precisione essenzialmente privi di significato . Sarebbe molto confuso se un debugger visualizzasse due variabili come detentori del valore “1.333333333333333” quando una di esse effettivamente deteneva 1.33333333333333319 e l’altra deteneva 1.33333333333333294 (il che significa che, sebbene avessero lo stesso aspetto, non erano uguali). La precisione extra mostrata dal debugger non è in grado di rappresentare un risultato di calcolo numericamente corretto, ma indica come il codice interpreterà i valori detenuti dalle variabili.