Articles

I pericoli dell’uso di tipi di dati float o reali

C’è una vecchia battuta sull’aritmetica in virgola mobile:

“Se taglio una torta in tre, ogni pezzo è 0,33 della torta. Se infilo tutti e tre i pezzi di nuovo insieme che mi dà 0,99 della torta. Dov’è finito il resto della torta?”
— ” Semplice. Questo è il po ‘bloccato sul coltello”

L’aritmetica in virgola mobile riguarda la tolleranza e la gestione dell’approssimazione per evitare errori di overflow nei calcoli. Nel mondo reale, di solito ci preoccupiamo della precisione nei numeri e, invece, sacrificheremo spazio e risorse per evitare il trabocco.

Mentre la scienza funziona felicemente entro un margine di errore, la precisione conta nella contabilità aziendale. Quando ero un programmatore cub, una volta ho scritto quello che pensavo essere un modo perfettamente adatto per calcolare il profitto delle offerte di agenti di borsa. In un milione di sterline, era un centesimo o due al massimo. Mi ha fatto molto piacere. Ha usato i calcoli inerenti al compilatore PL/1 che abbiamo usato al momento per sviluppare pacchetti finanziari. Ho mostrato loro l’applicazione finemente artigianale, ed erano inorriditi. Un centesimo in un milione di sterline sembrava ai commercianti della città hardboiled di essere spericolato. Non l’avrebbero avuto. Sono stato costretto a scrivere un pacchetto binary-coded-decimal (BCD) in codice assembler che era preciso.

SQL Prompt ha una regola di analisi del codice (BP023) che ti avviserà dell’uso diFLOAT oREAL tipi di dati, a causa delle inesattezze significative che possono introdurre al tipo di calcoli che molte organizzazioni eseguiranno abitualmente sui loro dati SQL Server.

Tipi di dati a numero approssimativo

L’aritmetica a virgola mobile è stata concepita in un momento in cui era prioritario risparmiare memoria dando un modo versatile di eseguire calcoli che coinvolgevano grandi numeri. Sebbene sia ancora utile per molti tipi di calcoli scientifici, in particolare quelli conformi allo standard IEEE 754 a doppia precisione per l’aritmetica in virgola mobile, è, necessariamente, un compromesso. L’indizio è nel nome di questo tipo di dati e aritmetica: ‘approssimativo’. I numeri in virgola mobile non possono rappresentare con precisione tutti i numeri reali: inoltre, le operazioni in virgola mobile non possono rappresentare con precisione tutte le operazioni aritmetiche. Tuttavia, l’intervallo di grandezza del numero che possono contenere è molto maggiore di quello possibile in altri tipi numerici, anche se non è sempre tenuto con precisione.

I problemi che derivano dall’uso di calcoli in virgola mobile sono dovuti al round-off durante calcoli complessi, e sono più spesso visti se i dati sono “mal condizionati”, in modo che piccoli cambiamenti nell’input vengano ingranditi nell’output. Le imprecisioni sono molto meno evidenti con una maggiore precisione della rappresentazione dei numeri, ma sono ancora presenti, comunque. Ci sono anche alcune restrizioni esoteriche nell’uso di numeri che sono validi ma non possono essere rappresentati in virgola mobile, come tan(π/2), ma è probabile che eccitino solo i matematici.

Tipi di dati in virgola mobile SQL Server

Lo standard SQL ha tre tipi di dati approssimativi in virgola mobile,REALDOUBLEPRECISION e FLOAT(n). SQL Server è conforme a questo tranne che non ha alcun tipo di dati DOUBLEPRECISION, utilizzando invece FLOAT(53). I tipi di dati FLOAT(24) e FLOAT(53) corrispondono a Binary32 (Singolo) e Binary64 (doppio) nello standard IEEE 754 e sono memorizzati in 4 e 8 byte e 7 e 16 cifre tenute, di conseguenza. Sono utili quando è importante che i calcoli producano lo stesso risultato di un’applicazione che utilizza.NET framework che utilizza anche IEEE 754. Il tipo a doppia precisione è richiesto anche quando i numeri superano nella loro grandezza il massimo consentito dal tipo di datiDECIMAL (38 cifre) anche se con perdita di precisione. I numeri approssimativi non possono, ovviamente, essere utilizzati in modo affidabile in qualsiasi test di uguaglianza, ad esempio una clausola WHERE.

Calcoli utilizzando il tipo di dati REALE (precisione singola)

Proverò il tipo di dati REAL. Il tipo di datiFLOAT(24), o più piccolo, reagisce allo stesso modo. La prima cosa da ricordare quando si sperimenta con numeri in virgola mobile in SQL Server è che SSMS rende un numero in virgola mobile in un modo che nasconde piccole differenze. Per esempio:

1
SELEZIONARE Convert(REALE,0.100000001490116119384765625)

…dà 0.1

Per vedere in modo più accurato, il valore che viene memorizzato in un numero in virgola mobile, è necessario utilizzare la funzione STR (), specificando la precisione che vuoi.

1
2
3
4
5

/*so di essere un po ‘ ingiusto qui, perché il numero decimale 0.1 non è rappresentabile
in virgola mobile; l’esatta rappresentazione binaria sarebbe una “1100” sequenza
continuare all’infinito:*/
DECLARE @FirstApproximate REALE = 0.1
SELEZIONARE Str(@FirstApproximate,20,16) –deve essere 0.100000001490116119384765625

Già, questo è allarmante. Dopo tutto, abbiamo a che fare con dati con milioni di righe in modo che piccoli errori si accumulino a meno che, come gli “arrotondamenti dei banchieri”, non facciano la media. Questo errore è già vicino al’ penny in un milione di sterline ‘ (1/ 240000000) che ho menzionato nell’introduzione!

Evitiamo 0.1 e lo mettiamo in un freak di virgola mobile. Che ne dici di dividere 1 per 3. Sicuramente questo non potrebbe essere un problema?

1
2
3
4
5

AFFERMANDO @saldi REALE = 1
AFFERMANDO @divisore REALE =3
SELEZIONARE Str(@saldi /@divisore,20,16) come quoziente
–produce 0.3333333432674408
–dovrebbe essere 0.3333333333333333

Oops. Si è sbagliato. OK, è un piccolo errore, ma ricorda la mia storia sui banchieri. Una risposta è giusta o è sbagliata, non ci sono sfumature di grigio per gli uomini in abiti grigi. Nella scuola di business, c’è solo un segno di spunta e una croce. Nessun segno che significa ‘abbastanza vicino’.

Un semplice test consiste nel dividere uno per numeri da uno a venti. Cosa potrebbe andare storto?

Possiamo memorizzare i risultati del calcolo in virgola mobile e numerico, entrambi convertiti in stringhe e quindi confrontare le stringhe (attenzione che STR() può mettere in uno spazio iniziale che crea una complicazione).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

DECLARE @divisor REAL, @dividend REAL = 1
DECLARE @FloatingPointCalculations TABLE (Divisor INT, QuotientFloatingPoint VARCHAR(20), QuotientNumeric VARCHAR(20));
DECLARE @ii INT = 1;
DECLARE @iiMax INT = 20;
WHILE (@ii <= @iiMax)
BEGIN
SELECT @divisor = @ii;
INSERT INTO @FloatingPointCalculations (Divisor, QuotientFloatingPoint,
QuotientNumeric)
SELECT @ii AS divisor, Str(@Dividend / @divisor, 20, 16) AS QuotientFloatingPoint,
Convert(VARCHAR(20), 1.0000000 / @ii) AS QuotientNumeric;
SELECT @ii += 1;
END;
SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations AS The;

Now, what if we list the rows where the numbers don’t match?

1
2
3

SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations The
WHERE Left(LTrim(The.QuotientFloatingPoint),16)<> Left(LTrim(The.QuozientNumeric), 16)

Ugh! Solo dove il divisore era 1, 2, 4, 8 o 16 c’era un risultato corretto.

Nel caso in cui speri che in qualche modo il float fosse accurato e la versione numerica non lo fosse, ecco il quoziente numerico calcolato in Excel:

Calcoli usando FLOAT(25) o over (double precision)

Se si utilizza double precision floating point, FLOAT(25) o over, i test vengono tutti superati, perché la funzione STR() consente un massimo di sedici posti a destra del punto decimale. Se ce ne sono più di 16, il risultato viene troncato. Il tipo di dati a doppia precisione ha sedici cifre, mentre il tipo di dati a precisione singola ne ha sette. Avrai anche visto che il singolo tipo di dati di precisione ottiene le prime sette cifre giuste. Allo stesso modo, la doppia precisione ottiene le prime sedici cifre giuste. Possiamo solo ampliare il numero per vedere l’approssimazione.

1
2

DECLARE @FirstApproximate FLOAT(53) = 10000000000000000.1
SELEZIONARE Str(@FirstApproximate,40,16) COME BigNumberWithaDecimal

Che la parte frazionaria è scomparso, non credi? Probabilmente è solo una piccola differenza, ma in alcuni calcoli può causare problemi.

Conclusione

L’aritmetica in virgola mobile è veloce ed economica sullo storage ma fornisce un risultato approssimativo. È adatto per applicazioni scientifiche ben condizionate, ma non per calcoli finanziari, che richiedono che un numero sia “giusto” o “sbagliato”. Ha anche lo svantaggio aggiuntivo in un database perché non è possibile testare in modo affidabile e coerente due numeri approssimativi per l’uguaglianza.

Non è corretto dire che non si dovrebbero mai usare numeri in virgola mobile nei tipi di dati SQL o in aritmetica. I tipi approssimativi sono presenti nello standard SQL per uno scopo. Oggigiorno continuerei sempre con il tipo di dati a virgola mobile a doppia precisione in SQL Server, dove esiste un requisito adeguato. Sono ottimi per scopi come la modellazione di sistemi meteorologici o la tracciatura di traiettorie, ma non per i tipi di calcoli per i quali è probabile che l’organizzazione media utilizzi un database.

Se si individua un uso errato di questi tipi, è necessario passare a un tipo DECIMALNUMERIC. Se sai che hai bisogno di aritmetica in virgola mobile e puoi spiegare perché, allora probabilmente ne sai abbastanza per evitare le insidie della virgola mobile, come quella che si è verificata nel famoso fallimento del missile Patriot che ha portato direttamente a 28 morti.