In questa rubrica ho lamentato innumerevoli volte l'eccessiva lunghezza e la cattiva implementazione del formato in virgola mobile più utilizzato, quello a 64 bit "double precision". La precisione di 15 cifre decimali e l'esponente che supera 300 in valore assoluto sono debordanti per tutti gli utilizzi pratici e la decisione di utilizzare 53 bit per il significando implica che la precisione sfiora le 16 cifre decimali senza raggiungerla, uno vero "spreco"!

 In effetti, nel 99% delle situazioni, se escludiamo alcune particolari branche della matematica o della fisica destinate ad un uso di nicchia, il numero massimo di cifre significative che servono realmente non supera mai la dozzina, mentre l'esponente massimo rimane generalmente ben sotto il centinaio. Anche se avere intervalli molto più ampi su entrambi questi fronti può essere confortante, perchè ci garantisce ampissimi margini e ulteriori garanzie di precisione, non c'è una reale necessità nell'abbondare e questo significa che la complessità degli attuali processori, il loro consumo energetico e la quantità di memoria utilizzata potrebbero essere ottimizzati con vantaggio.

 Se passassimo da parole di 64 bit a parole di 48 bit, il numero di transistor necessari a realizzare una unità logico-aritmetica calerebbe del 25% o quasi, con analogo risparmio sul consumo energetico e con riduzione di tutte le problematiche associate alla dissipazione del calore. Con un formato a 52 bit, il risparmio sarebbe più contenuto ma comunque significativo (-19%). D'altro canto, 12 cifre significative sono davvero parecchie: esse consentono di descrivere il sistema Terra-Luna con una precisione migliore del millimetro, oppure a fornire la posizione esatta di ogni atomo nel corpo umano e di ogni stella nella Via Lattea (l'errore, in entrambi i casi, sarebbe inferiore o confrontabile alle dimensioni degli oggetti in questione, atomi o stelle). Del resto, come già ribadito più volte, tutte le costanti fisiche sono misurate con un livello di precisione che non va oltre 12 cifre significative e, anzi, in genere ci si ferma sulle 10 cifre.

 Di seguito, quindi, verranno descritti due possibili sistemi floating point, uno a 52 e uno a 48 bit. Naturalmente il sottoscritto preferisce il primo perchè fornisce una precisione ottimale, anche se le parole di 48 bit sono probabilmente più facili da gestire (essendo 48 multiplo di 8 e di 16), oltre ad incrementare i vantaggi suddetti in termini di complessità strutturale, consumi energetici e smaltimento del calore. Per ciascuna delle due lunghezze, vengono fornite due possibili opzioni. La prima opzione è una semplice implementazione ortodossa del formato in virgola mobile, conforme agli standard già esistenti, compreso l'utilizzo di numeri piccoli "denormalizzati". L'opzione alternativa o "potenziata", invece, offre il notevole vantaggio di poter estendere il range dell'esponente, anche oltre il massimo consentito dalla doppia precisione tradizionale, sebbene al costo di una lieve riduzione in precisione di cui si tiene comunque traccia; è prevista inoltre la possibilità di codificare anche numeri interi e, più in generale, razionali. Si è invece rinunciato alla possibilità, pure allettante, di codificare "numeri grandissimi" o numeri immaginari, perchè reputata poco utile o efficace, a fronte di una notevole complessità di implementazione (per ulteriori dettagli e spiegazioni su queste terminologie e tecniche di codifica, si rimanda agli articoli già scritti in precedenza, specialmente il primo).

 

I formati IEEE-compatibili

 Ben poco c'è da dire sulle versioni ortodosse del Floating point a 48 e 52 bit. Una volta fissata la opportuna quota di 10 bit per l'esponente (il cui valore decimale oscillerà quindi nel range ottimale ±154) ci ritroviamo con un significando di 11,44 e 12,64 cifre decimali, rispettivamente. Naturalmente, rimane in vigore il ricorso ai numeri “denormalizzati” (cioè numeri piccoli a “virgola fissa”) quando l'esponente è minimo e ai valori ±∞ oppure NaN (Not a Number) se il campo esponente assume il valore massimo. Come si vede, si tratta di prestazioni già ottimali, ma volendo si può andare oltre!

 

I formati "potenziati"

 Entrambe le opzioni partono sempre da un esponente di 10 bit, ma la sua reale estensione, come pure la reale precisione del significando, mutano agli estremi secondo una tecnica già illustrata in un precedente articolo. In pratica, quando l'esponente, in valore assoluto supera una soglia prestabilita, allora alcuni bit del significando vengono "sacrificati" per aumentare il range dell'esponente, a scapito della precisione. I due standard qui illustrati contengono la lettera finale "p" proprio per indicare il "potenziamento" dell'esponente e per distinguerli dai precedenti standard proposti, che invece recavano la lettera "e" terminale. l'aggiunta di un eventuale numero dopo un trattino sta ad indicare invece il valore approssimativo dell'esponente espanso massimo (o minimo) raggiunto da quella particolare opzione.

 Nel caso del "48p", i bit sacrificati sono 4 e questo consente di avere 24=16 sotto-valori di esponente per ciascuno dei rimanenti valori di E a disposizione. Qui si è deciso di adottare come soglia l'esponente binario 400, corrispondente all'esponente decimale 120 (sempre in valore assoluto). Ne segue la tabella di decodifica degli esponenti qui sotto, dove la dicitura "48p-500" fa riferimento all'esponente massimo decimale "espanso" che tocca ±530, sbaragliando persino il formato tradizionale di doppia precisione!

48p500 corretto

 Detto in altro modo, fino a che l'esponente si mantiene entro l'intervallo ±120 (comunque molto ampio), la precisione è di 11 cifre significative, scendendo a 10 quando l' esponente va oltre. Esistono naturalmente anche altre opzioni, quella estrema potrebbe essere la seguente, con un esponente massimo "non espanso" pari a 94 e un esponente espanso di circa 960 (la potremmo chiamare opzione "48p-1000"):

 48p1000 corretto

 Nel caso "52p", invece, il maggiore numero di bit consentirebbe di sacrificare ben 7 bit di significando in modalità estesa. Questo porta a ridurre la precisione sempre a 10 bit, ma stavolta con un margine migliore (pari a 0,54 cifre decimali) e potendo incrementare l'esponente massimo senza impattare troppo su quello non esteso. La tabella in apertura mostra l'opzione "52p-1000", preferita dal sottoscritto, con un esponente che raggiuge il valore massimo di 146 in valore assoluto (±484 come esponente di 2) e oltre 1000 in modalità estesa (da -3327 a +3328 in binario).

52p4000

 Anche qui, si potrebbe pensare ad una versione "estrema" con precisione piena fino ad un esponente pari a 120 e poi, con precisione ridotta, un esponente potenziato che raggiunge 4200 (opzione "52p-4000"). La tabella qui sopra descrive la codifica e, decisamente, non ha senso andare oltre!

 

Numeri Razionali

  L'altro considerevole vantaggio di un sistema "fuori standard" consiste nell'eliminazione dei numeri "denormalizzati" che non avrebbero più senso data la notevole estensione del sistema verso numeri piccolissimi. Quindi il valore minimo di E (equivalente a una sequenza di 10 zeri) viene dedicato alla codifica di numeri razionali (frazioni esatte) e, tra essi, i numeri interi, normalmente non contemplati nel formato in virgola mobile che è rivolto a rappresentare soprattutto numeri irrazionali e comunque approssimati.

 Una annotazione importante qui va fatta sul bit di segno. Apparentemente, nel paragrafo precedente è stato ignorato o, meglio, sembrava "accorpato" nel significando. Esso in realtà c'è sempre e conserva la sua autonomia. Semplicemente, il significando era aumentato di una unità perchè nel formato Floating Point tradizionale (e anche in questo espanso) si ricorre al "trucco" dell'hidden bit, ovvero la cifra binaria più significativa non è codificata ma è sottintesa dal momento che deve essere sempre pari a uno!  Adesso, per i numeri razionali, questo trucco non si può usare e il segno va comunque considerato e quindi tenuto fuori dal computo delle possibili combinazioni di codifica utilizzabili. 

 A conti fatti, quindi, in tutti i formati "potenziati" finora descritti, quando E=0 disponiamo di 33 bit utili nel caso "48p" e 34 bit utili per i formati "52p". La mia proposta è quella di fare un uso molto limitato del floating slash (linea di frazione mobile) dedicando alla  sua codifica solo 1 bit nel primo caso e 2 nel secondo, ottenendo alla fine sempre 32 bit efficaci per codificare i valori di numeratore e denominatore. Questa decisione deriva dal fatto che, a mio parere, avere numeratore e denominatore di lunghezze molto diverse tra loro non è sempre possibile, quindi i casi in cui si trarrebbe vantaggio da questa tecnica sono una minoranza. Unica eccezione importante sono, naturalmente, i numeri interi che poi sarebbero razionali con denominatore unitario. Ecco perchè l'unico bit dedicato alla "slash position" nei sistemi "48p" serve a dire se siamo in presenza o meno di un intero. Nel primo caso, i rimanenti 32 bit codificano un numero compreso nell'intervallo:

 n = 0 ÷ 232-1 = 0 ÷ 4294967295

 L'opzione rimanente è quella di avere uno "slash" fisso esattamente a metà del significando, avendo quindi a disposizione 16 bit per il numeratore e 16 per il denominatore (con l'avvertenza che entrambi partono da 1 e non da zero e quindi arrivano al massimo a 65536). Per il "52p" abbiamo a disposizione un altro "bit segnalatore" e quindi altre due opzioni poste simmetricamente, in modo da avere di un numeratore da 24 bit e un denominatore da 8 oppure il viceversa; di fatto, questo significa specificare con 2 bit quanti dei 4 bytes disponibili sono dedicati al numeratore e quanti al numeratore.

 Naturalmente, il sistema può essere reso più efficiente se si è in grado di ridurre la frazione ai minimi termini e si è in grado di codificarla di conseguenza. Una semplice idea potrebbe essere quella di codificare il numeratore in modo da eliminare tutti i multipli interi del denominatore, ma questo stratagemma è poco efficiente se il denominatore è un numero grande e, comunque, non elimina i casi in cui numeratore e denominatore abbiano fattori primi comuni (frazione riducibile). Esistono algoritmi appositi per la riduzione di frazioni usando il massimo comune divisore o, in alternativa, ricorrendo al metodo delle frazioni continue, ma qui non ci addentreremo in queste possibilità, la cui implementazione risulterebbe probabilmente troppo complicata e costosa in termini di complessità circuitale e forse anche di consumi energetici.

 

Conclusione

 Si tenga sempre presente che, per come è realizzata la decodifica dei sistemi "potenziati" qui presentati, il risultato numerico conserva sempre "memoria" dell'eventuale degrado di precisione incontrato nella catena di calcoli utilizzati per ottenerlo, conservando una codifica espansa anche se quel numero non risulta grandissimo o piccolissimo! Questa "marcatura" sulla precisione è importante e la si ottiene a scapito di una "ridondanza" dei valori numerici rappresentati con esponente normale ed esponente ampliato, dato che le due codifiche si sovrappongono. Naturalmente, sia l'esponente espanso che l'inclusione dei numeri razionali implicano una complicazione dell'hardware, quindi la loro reale convenienza deve essere attentamente valutata e non è questa la sede per farlo. Tuttavia, reputo le proposte come spunti interessanti di riflessione, a prescindere dalla possibile implementazione.

 

Nota a piè pagina: le due tabelle relative ai formati 48p presentavano delle inesattezze e sono state modificate nel dicembre 2023