La prima volta un 0.1 + 0.2 === 0.3 bug mi ha morso in produzione, ho fatto quello che fa la maggior parte degli sviluppatori: ho cercato, trovato & quot; il punto di fluttuazione è strano, & quot; e sono andato avanti Sono passati anni prima che guardassi effettivamente i bit e capissi perché Una volta fatto, un'intera categoria di bug numerici ha smesso di essere misteriosa Questa guida attraversa ciò che ti mostra un convertitore IEEE 754, usando il Convertitore IEEE 754 su Toolz.dev, e perché fissare il segno, l'esponente e la mantissa è il modo più veloce per costruire una vera intuizione su come i computer memorizzano i decimali.
tl; dr: I computer memorizzano numeri in virgola mobile come segno, una potenza di due e un significato frazionario, seguendo lo standard IEEE 754 Un convertitore IEEE 754 codifica un decimale come 3.14159 in quei bit esatti e decodifica i bit di nuovo in un valore Il Convertitore IEEE 754 fa entrambe le cose, per precisione singola e doppia, interamente nel tuo browser, con il segno, l'esponente e la mantissa scomposti.
Cos'è IEEE 754?
IEEE 754 è lo standard, pubblicato per la prima volta nel 1985 e rivisto come IEEE 754-2019che definisce come i computer rappresentano numeri in virgola mobile in binario Quasi ogni CPU, GPU e linguaggio di programmazione lo segue, motivo per cui un double si comporta allo stesso modo in C, Java, Python e JavaScript Quando le persone dicono & quot; virgola mobile, & quot; quasi sempre significano virgola mobile binaria IEEE 754.
L'idea centrale è che un numero è memorizzato in tre parti C'è un bit di segno che dice positivo o negativo C'è un esponente che scala il valore di una potenza di due E c'è una mantissa, chiamata anche frazione o significando, che contiene le cifre precise Messo insieme, un numero normale è segno per 1, mantissa per 2 all'esponente È notazione scientifica, in base due, impacchettata in un numero fisso di bit.
I due formati che incontri quotidianamente sono a precisione singola, chiamati binary32, e a doppia precisione, chiamati binary64 Il singolo utilizza 1 bit di segno, 8 bit di esponente e 23 bit di mantissa, per un totale di 32 bit Il doppio utilizza 1 bit di segno, 11 bit di esponente e 52 bit di mantissa, per un totale di 64 bit Il doppio è il tipo a virgola mobile predefinito nella maggior parte delle lingue; il singolo è comune nelle GPU e nel codice sensibile alla memoria.
Cosa fa effettivamente un convertitore IEEE 754?
Un convertitore IEEE 754 prende un numero e ti mostra il suo esatto schema di bit, suddiviso in questi tre campi, insieme alla forma esadecimale e al valore letto dai bit. Funziona in entrambi i modi: inserisci un decimale per codificarlo o incolla un modello di bit binario o esadecimale per decodificarlo nuovamente nel numero che rappresenta.
Su Toolz.dev il flusso di lavoro è breve Scegli precisione singola o doppia Scegli se il tuo input è un numero decimale, un modello di bit esadecimale o un modello di bit binario Inserisci il valore Lo strumento mostra il bit di segno, il campo esponente, la mantissa, i bit completi, l'esagono, sia l'esponente polarizzato che quello imparziale, e l'esatto valore memorizzato Ogni campo ha un pulsante di copia.
Il motivo per guardare tutto questo, piuttosto che fidarsi del numero che la tua lingua stampa, è che il numero stampato si trova per omissione Quando la tua console mostra 0.1, ti sta mostrando un rendering arrotondato e amichevole I bit ti dicono cosa è realmente memorizzato, e la differenza tra questi due è dove vivono i bug in virgola mobile.
Perché 0,1 più 0,2 non è uguale a 0,3?
Questo è l'esempio canonico, e il convertitore lo rende concreto Il valore 0,1 non può essere rappresentato esattamente in virgola mobile binaria, per lo stesso motivo 1/3 non può essere scritto esattamente in decimale In base dieci, 1/3 è 0,3333... per sempre In base due, 0,1 è una frazione ripetitiva che non termina mai, quindi deve essere arrotondata per adattarsi a 52 bit di mantissa.
Decodificare il doppio per 0,1 e il valore effettivamente mantenuto dai bit è 0,100000000000005551151231257827021181583404541015625. Il doppio per 0,2 è allo stesso modo un capello troppo grande Aggiungere i due valori arrotondati e il risultato è un po' più di 0,3, ma il doppio per 0,3 è un po' meno, quindi i due non sono uguali Niente è rotto Ogni passaggio sta facendo esattamente ciò che richiede IEEE 754 e il convertitore ti consente di guardarlo accadere invece di prenderlo con fiducia.
Una volta che hai visto questo, la correzione segue naturalmente: mai confrontare i float per l'uguaglianza esatta Confronta all'interno di una piccola tolleranza, o usa centesimi interi per soldi, o una libreria decimale quando hai bisogno di aritmetica decimale esatta Il bug non era mai nell'aggiunta Era nell'aspettarsi un formato binario per memorizzare esattamente le frazioni decimali.
Quali sono il segno, l'esponente e la mantissa?
Ogni campo ha un lavoro specifico e il convertitore li etichetta tutti e tre.
Il bit di segno è il più semplice: 0 significa positivo, 1 significa negativo Poiché il segno è un bit separato, IEEE 754 ha sia uno zero positivo che uno negativo, che si comportano allo stesso modo nei confronti ma portano bit diversi.
Il campo esponente memorizza una potenza di due, ma con una polarizzazione aggiunta in modo da poter rappresentare esponenti negativi senza un segno separato La polarizzazione è 127 per la precisione singola e 1023 per il doppio Quindi un campo esponente memorizzato di 127 in precisione singola significa un esponente effettivo pari a zero Il convertitore Toolz.dev mostra entrambi i numeri: il valore distorto che vive nei bit e la potenza imparziale di due si applica effettivamente Quella distinzione fa inciampare molte persone, motivo per cui lo strumento lo scrive piuttosto che farti sottrarre nella tua testa.
La mantissa contiene la parte frazionaria del significando Per un numero normale c'è un'implicita iniziale 1 che non viene memorizzata, perché un significando binario normalizzato inizia sempre con 1, quindi il formato ottiene un bit libero di precisione tralasciandolo Ecco perché la precisione singola dà circa 7 cifre decimali e il doppio dà circa 15 a 16, anche se i campi di mantissa sono 23 e 52 bit.
Ecco come i due formati si allineano campo per campo:
| Proprietà | Singolo (binario32) | Doppio (binario64) |
|---|---|---|
| Bit totali | 32 | 64 |
| Bit di segno | 1 | 1 |
| Bit esponenti | 8 | 11 |
| Mantissa bit | 23 | 52 |
| Bias dell'esponente | 127 | 1023 |
| Cifre decimali ca | 7 | dalle 15 alle 16 |
| Nome comune | galleggiare | doppia |
Come faccio a riconvertire i bit in un numero decimale?
La decodifica è utile tanto quanto la codifica, ed è il modo in cui si legge un valore da un dump di memoria, un formato di file binario o un protocollo di rete che memorizza i float grezzi Imposta il tipo di input su Hex o Binary e incolla l'esatto modello di bit che la precisione si aspetta: 8 cifre esadecimali o 32 bit per singolo, 16 cifre esadecimali o 64 bit per doppio.
Ad esempio, l'esagono a precisione singola 40490FDB decodifica in 3.1415927, il float più vicino a pi. Il convertitore accetta anche un optional 0x prefissa e ignora spazi e trattini bassi, in modo da poter incollare i byte raggruppati man mano che li trovi Se gli dai il numero sbagliato di cifre, ti dice esattamente di quante ne servono quelle precision piuttosto che troncare silenziosamente, che è il tipo di messaggio di errore che vorrei che più strumenti si preoccupassero di scrivere.
Questa direzione di decodifica si accoppia naturalmente con un generale Convertitore di numeri quando è necessario spostare un valore tra decimale, esadecimale, binario e ottale senza l'interpretazione in virgola mobile e con il traduttore binario quando lavori con il testo piuttosto che con i numeri, li raggiungo abbastanza spesso da farli vivere nello stesso angolo dei miei segnalibri.
Come sono rappresentati l'infinito, NaN e zero?
I valori speciali sono quelli in cui IEEE 754 diventa intelligente e il convertitore identifica ogni caso per te in modo da non dover memorizzare i modelli.
Il campo dell'esponente funge da interruttore Quando ogni bit dell'esponente è 1, ci si trova nell'intervallo speciale: una mantissa tutta zero significa infinito, e qualsiasi mantissa diversa da zero significa NaN, non un numero L'infinito positivo e negativo differiscono solo per il bit di segno NaN è ciò che si ottiene da operazioni come zero diviso per zero o la radice quadrata di un negativo, e ha la famosa proprietà che non si eguaglia.
Quando ogni bit esponente è 0, ci si trova nell'altro intervallo speciale: una mantissa tutta zero è zero, positiva o negativa a seconda del bit di segno, e una mantissa diversa da zero è un numero subnormale I subnormali riempiono il divario tra zero e il numero normale più piccolo, scambiando precisione per la capacità di rappresentare magnitudini molto piccole Usano un esponente effettivo di uno meno la distorsione e lasciano cadere l'inizio implicito 1, di cui il convertitore tiene conto quando mostra l'esponente imparziale.
Tutto il resto, dove il campo dell'esponente non è né tutti zeri né tutti uno, è un numero normale Poter dare un'occhiata a un modello di bit e sapere istantaneamente quale di questi cinque casi si sta guardando è un vero e proprio superpotere di debug quando un calcolo produce un valore che non ci si aspettava.
Quando ho effettivamente bisogno di questo?
Ne hai bisogno più spesso di quanto indovineresti una volta che sai guardare. Lo raggiungo quando un risultato numerico è disattivato da un errore di arrotondamento e voglio confermare se il valore è esattamente rappresentabile. Lo uso quando scrivo o eseguo il debug di un serializzatore binario, di un formato di file o di un protocollo wire che memorizza i float, quindi posso verificare che i byte corrispondano a ciò che intendevo. È indispensabile quando si confronta la precisione singola e doppia, ad esempio decidere se uno shader GPU può utilizzare float senza perdere troppa accuratezza Ed è uno dei sussidi didattici più chiari che conosco per l'architettura dei computer, perché vedere i campi rende tangibile lo standard astratto.
Se si costruisce attraverso lo stack nel modo in cui lo faccio io, la virgola mobile si presenta in luoghi sorprendenti: un calcolo del prezzo in un'API Laravel, un'animazione su tela in React, un'esportazione CSV che manipola una coordinata Comprendere il formato di archiviazione è un piccolo investimento che ripaga su tutti loro Ho scritto di come strumenti come questo si inseriscono in un kit più ampio nel Toolkit per sviluppatori web, e il guida del convertitore di base numerica e guida al traduttore binario coprire le conversioni di basso livello vicine in modo più approfondito.
Quali sono i limiti della precisione in virgola mobile?
Una volta che puoi vedere i bit, i limiti del formato smettono di essere astratti Un doppio ha 52 bit di mantissa memorizzati più l'inizio implicito 1, che funziona a circa 15-16 cifre decimali significative Spingi oltre e la precisione evapora silenziosamente L'intero più grande che un doppio può rappresentare con ogni valore intermedio ancora esatto è 2 alla 53a potenza, che è 9007199254740992. Aggiunge uno a quello e il risultato arrotonda indietro, perché il valore rappresentabile successivo è due, non uno. Questo è il motivo per cui i linguaggi che utilizzano il doppio per tutti i numeri, JavaScript tra loro, espongono un Number.MAX_SAFE_INTEGER costante esattamente a quel valore e perché gli ID di database di grandi dimensioni inviati come numeri JSON possono corrompersi silenziosamente.
Il divario tra un valore rappresentabile e il successivo è chiamato unità nell'ultimo posto, o ULP, e cresce man mano che la magnitudine cresce Vicino a 1,0 il divario è minuscolo; vicino al miliardo è qualche centinaio; vicino al massimo doppio, circa 1,8 volte 10 al 308°, il divario tra vicini è astronomicamente grande Il convertitore lo rende visibile: codifica due grandi numeri vicini e spesso scoprirai che condividono gli stessi bit, perché semplicemente non c'è rappresentazione tra loro Capire ULP è ciò che impedisce di aspettarsi una risoluzione maggiore di quella che il formato può dare.
Una trappola correlata è la cancellazione catastrofica Quando si sottraggono due numeri in virgola mobile quasi uguali, le cifre iniziali si annullano e si rimane con i bit di ordine basso, che erano la parte meno accurata per cominciare Il risultato può essere dominato dall'errore di arrotondamento anche se ogni input sembrava preciso Questo è il motivo per cui il codice numericamente attento riordina le operazioni per evitare di sottrarre quantità vicine, e perché sommare una lunga lista di float in un ciclo ingenuo accumula errori che algoritmi migliori come la sommatoria di Kahan evitano.
I takeaway pratici sono coerenti Non memorizzare il denaro come float; utilizzare centesimi interi o un tipo decimale, perché un valore come 0,10 non è esattamente rappresentabile e gli errori si compongono in migliaia di transazioni Non confrontare i float con l'uguaglianza esatta; confrontare all'interno di una tolleranza dimensionata al tuo problema E quando un intero molto grande deve sopravvivere a un viaggio di andata e ritorno, tenerlo come stringa o utilizzare un tipo intero di precisione arbitraria piuttosto che fidarsi di un doppio per tenerlo Ognuna di queste regole è più facile da ricordare una volta che il convertitore ti ha mostrato perché il formato si comporta in quel modo.
Domande frequenti
Come faccio a convertire un numero decimale in IEEE 754?
Scegli la precisione singola o doppia, mantieni il tipo di input su Decimale, digita il tuo numero e converti Lo strumento codifica il valore nel suo modello IEEE a 754 bit e mostra il bit del segno, il campo dell'esponente, la mantissa, il binario completo e l'esadecimale. Tutto viene eseguito nel tuo browser.
Qual è la differenza tra precisione singola e doppia?
La precisione singola (binaria32) utilizza 32 bit: 1 segno, 8 esponente e 23 mantissa, fornendo circa 7 cifre decimali di precisione La doppia precisione (binaria 64) utilizza 64 bit: 1 segno, 11 esponente e 52 mantisse, fornendo circa 15-16 cifre. Il doppio è il tipo float predefinito nella maggior parte delle lingue; il singolo è comune sulle GPU e nel codice con limiti di memoria.
Perché 0.1 non si converte in un valore esatto?
0,1 non ha rappresentazione finita in virgola mobile binaria, così come 1/3 non ha decimale finito Il doppio rappresentabile più vicino è 0,10000000000005551151231257827021181583404541015625. il convertitore mostra il valore memorizzato riletto dai bit, quindi puoi vedere la piccola differenza che causa sorprese di arrotondamento come 0,1 + 0,2 che non equivalgono a 0,3.
Quali sono il segno, l'esponente e la mantissa?
Il bit di segno è 0 per positivo e 1 per negativo Il campo dell'esponente memorizza una potenza di due con una polarizzazione aggiunta (127 per singolo, 1023 per doppio), motivo per cui lo strumento mostra sia il valore distorto nei bit che la potenza imparziale che applica La mantissa contiene la parte frazionaria del significando, con un implicito iniziale 1 per i numeri normali.
Posso riconvertire i bit IEEE 754 in un numero decimale?
Sì. Impostare il tipo di input su esadecimale o binario e incollare il modello di bit esatto: 8 cifre esadecimali o 32 bit per precisione singola, 16 cifre esadecimali o 64 bit per doppio Lo strumento lo decodifica e mostra il valore decimale che rappresenta insieme alla ripartizione del campo completo.
Come sono rappresentati l'infinito, NaN e zero?
Quando ogni bit esponente è 1, una mantissa tutta zero significa infinito e una mantissa diversa da zero significa NaN. Quando ogni bit esponente è 0, una mantissa tutta zero è zero (positiva o negativa a seconda del bit di segno) e una mantissa diversa da zero è un numero subnormale. Il convertitore etichetta ciascuno di questi casi per te.
È sicuro utilizzare questo strumento per i numeri sensibili?
Sì. La conversione viene eseguita interamente in JavaScript nel browser Nessuna richiesta di rete trasporta il tuo input, nulla viene memorizzato e lo strumento funziona offline una volta caricata la pagina, quindi qualsiasi valore immesso rimane sul tuo dispositivo.
Quale formato esadecimale si aspetta il convertitore?
Cifre esadecimali semplici, con un prefisso 0x opzionale e spazi o trattini bassi per il raggruppamento, che vengono ignorati La precisione singola prevede 8 cifre esadecimali e la precisione doppia ne prevede 16 Ad esempio, 40490FDB è pi greco a precisione singola (3.1415927).
Esplora tu stesso i pezzi con il libero Convertitore IEEE 754. Codifica e decodifica float di precisione singoli e doppi interamente nel tuo browser, senza nulla caricato.



