Mysql
 sql >> Database >  >> RDS >> Mysql

Best practice per la memorizzazione dei pesi in un database SQL?

Affermi che ci sono imprecisioni intrinseche nei numeri in virgola mobile. Penso che questo meriti di essere esplorato un po' prima.

Quando si decide su un sistema numerico per rappresentare un numero (che sia su un pezzo di carta, in un circuito di computer o altrove), ce ne sono due separati questioni da considerare:

  1. la sua base; e

  2. il suo formato .

Scegli una base, qualsiasi base...

Limitato da uno spazio finito, non si può rappresentare un membro arbitrario di un insieme infinito . Ad esempio:non importa quanta carta acquisti o quanto piccola sia la tua calligrafia, sarebbe sempre possibile trovare un numero intero che non si adatta allo spazio dato (potresti semplicemente continuare ad aggiungere cifre extra fino a quando la carta si esaurisce). Quindi, con interi , di solito limitiamo il nostro spazio finito a rappresentare solo quelli che rientrano in un intervallo particolare, ad es. se abbiamo spazio per il segno positivo/negativo e tre cifre, potremmo limitarci all'intervallo [-999,+999] .

Ogni intervallo non vuoto contiene un insieme infinito di numeri reali. In altre parole, non importa quale intervallo si acquisisca sui numeri reali —che sia [-999,+999] , [0,1] , [0.000001,0.000002] o qualsiasi altra cosa:c'è ancora un insieme infinito di reali all'interno di quell'intervallo (è sufficiente continuare ad aggiungere cifre frazionarie (diverse da zero))! Pertanto i numeri reali arbitrari devono sempre essere "arrotondato" a qualcosa che può essere rappresentato in uno spazio finito.

L'insieme dei numeri reali che possono essere rappresentati nello spazio finito dipende dal sistema numerico utilizzato. Nel nostro (familiare) posizionale base-10 sistema, lo spazio finito sarà sufficiente per metà (0.510 ) ma non per un terzo (0.33333…10 ); al contrario, nella posizione (meno familiare) base-9 sistema, è il contrario (gli stessi numeri sono rispettivamente 0.44444…9 e 0.39 ). La conseguenza di tutto ciò è che alcuni numeri che possono essere rappresentati utilizzando solo una piccola quantità di spazio in base posizionale-10 (e quindi appaiono essere molto "rotondi" per noi umani), ad es. un decimo, richiederebbe in realtà infiniti circuiti binari per essere archiviati con precisione (e quindi non sembrano essere molto "tondi" ai nostri amici digitali)! In particolare, poiché 2 è un fattore di 10, lo stesso non vale al contrario:qualsiasi numero che può essere rappresentato con binario finito può anche essere rappresentato con decimale finito.

Non possiamo fare di meglio per quantità continue. In definitiva, tali quantità devono utilizzare una rappresentazione finita in alcuni sistema numerico:è arbitrario se quel sistema sembra essere facile sui circuiti dei computer, sulle dita umane, su qualcos'altro o sul nulla:qualunque sia il sistema utilizzato, il valore deve essere arrotondato e quindi sempre risulta in "errore di rappresentazione".

In altre parole, anche se si dispone di uno strumento di misura perfettamente accurato (cosa fisicamente impossibile), qualsiasi misura riportata sarà già stata arrotondata a un numero che si adatta al suo display (in qualunque base utilizzi, in genere decimale, per ovvi motivi). Quindi, "86,2 once" non è mai effettivamente "86,2 once " ma piuttosto una rappresentazione di "qualcosa tra 86,1500000... oz e 86,2499999... oz ". (In realtà, poiché in realtà lo strumento è imperfetto, tutto ciò che possiamo davvero dire è che abbiamo alcuni grado di confidenza che il valore effettivo rientri in quell'intervallo, ma questo si sta decisamente discostando in qualche modo dal punto qui).

Ma possiamo fare di meglio per quantità discrete . Tali valori non sono "numeri reali arbitrari" e quindi ad essi non si applica nessuno dei precedenti:possono essere rappresentati esattamente nel sistema numerico in cui sono stati definiti e, in effetti, dovrebbero essere (poiché la conversione in un altro sistema numerico e il troncamento a una lunghezza finita comporterebbe l'arrotondamento a un numero inesatto). I computer possono (inefficientemente) gestire tali situazioni rappresentando il numero come una stringa:ad es. considera ASCII o BCD codifica.

Applica un formato...

Poiché è una proprietà della base (piuttosto arbitraria) del sistema numerico, se un valore sembra essere "arrotondato" o meno non ha alcun effetto sulla sua precisione . Questa è un'osservazione davvero importante , che va contro l'intuizione di molte persone (ed è il motivo per cui ho passato così tanto tempo a spiegare la base numerica sopra).

La precisione è invece determinata da quanti cifre significative ha una rappresentanza . Abbiamo bisogno di un formato di archiviazione in grado di registrare i nostri valori almeno tante cifre significative quante le riteniamo corrette . Prendendo a titolo di esempio valori che riteniamo corretti quando indicati come 86.2 e 0.0000862 , le due opzioni più comuni sono:

  • Punto fisso , dove il numero di cifre significative dipende dalla grandezza :per esempio. nella rappresentazione fissa di 5 punti decimali, i nostri valori verrebbero archiviati come 86.20000 e 0.00009 (e quindi hanno rispettivamente 7 e 1 cifra significativa di precisione). In questo esempio, la precisione è andata persa in quest'ultimo valore (e infatti non ci vorrebbe molto di più per non essere stati totalmente incapaci di rappresentare nulla significativo); e il primo valore memorizzato falsa precisione , che è uno spreco del nostro spazio limitato (e in effetti, non ci vorrebbe molto di più perché il valore diventi così grande da traboccare la capacità di archiviazione).

    Un esempio comune di quando questo formato potrebbe essere appropriato è per un sistema contabile:le somme monetarie di solito devono essere tracciate al centesimo indipendentemente dalla loro grandezza (quindi è richiesta meno precisione per valori piccoli e più precisione è richiesta per valori grandi). Di solito anche la valuta è considerata discreta (i penny sono indivisibili), quindi questo è anche un buon esempio di una situazione in cui una base particolare (decimale per la maggior parte delle valute moderne) è desiderabile per evitare gli errori di rappresentazione discussi sopra.

  • Virgola mobile , dove il numero di cifre significative è costante indipendentemente dalla grandezza :per esempio. nella rappresentazione decimale a 5 cifre significative, i nostri valori verrebbero archiviati come 86.200 e 0.000086200 (e, per definizione, hanno 5 cifre significative di precisione entrambe le volte). In questo esempio, entrambi i valori sono stati memorizzati senza alcuna perdita di precisione; ed entrambi hanno anche lo stesso importo di falsa precisione, che è meno dispendiosa (e quindi possiamo utilizzare il nostro spazio finito per rappresentare una gamma di valori molto più ampia, sia grande che piccola).

    Un esempio comune di quando questo formato potrebbe essere appropriato è per la registrazione di qualsiasi misurazione del mondo reale :la precisione degli strumenti di misura (che tutti soffrono sia di sistematico e casuale errori) è abbastanza costante indipendentemente dalla scala, quindi, date cifre significative sufficienti (tipicamente intorno a 3 o 4 cifre), non si perde assolutamente alcuna precisione anche se un cambio di base ha comportato un arrotondamento a un numero diverso .

    Ma quanto sono precisi i formati di archiviazione in virgola mobile utilizzato dai nostri computer?

    La cosa più importante da capire è che questi formati superano rispettivamente diecimila e oltre un trilione volte più preciso piuttosto che dire "86.2", anche se le conversioni esatte del binario in decimale includono un'errata falsa precisione (che dobbiamo ignorare:ne parleremo tra poco)!

Nota anche che entrambi corretto e i formati a virgola mobile comporteranno una perdita di precisione quando un valore è noto con maggiore precisione rispetto a quanto supportato dal formato. Tali errori di arrotondamento può propagarsi in operazioni aritmetiche per produrre risultati apparentemente errati (il che senza dubbio spiega il tuo riferimento alle "imprecisioni intrinseche" dei numeri in virgola mobile):ad esempio, 3 × 3000 in 5 punti il ​​punto fisso produrrebbe 999.99000 anziché 1000.00000; e 7 − ⁄50 in 5 cifre significative il virgola mobile produrrebbe 0.0028600 anziché 0.0028571 .

Il campo dell'analisi numerica è dedicato alla comprensione di questi effetti, ma è importante rendersi conto che qualsiasi sistema utilizzabile (anche eseguendo calcoli nella tua testa) è vulnerabile a tali problemi perché nessun metodo di calcolo di cui è garantito il termine può mai offrire una precisione infinita :considera, ad esempio, come calcolare l'area di un cerchio:ci sarà necessariamente una perdita di precisione nel valore utilizzato per π, che si propagherà nel risultato.

Conclusione

  1. Le misurazioni del mondo reale dovrebbero utilizzare la virgola mobile binaria :è veloce, compatto, estremamente preciso e niente male (compresa la versione decimale da cui sei partito). Poiché tipi di dati in virgola mobile di MySQL sono IEEE754, questo è esattamente ciò che offrono.

  2. Le applicazioni di valuta dovrebbero utilizzare il punto fisso denario :mentre è lento e spreca memoria, assicura sia che i valori non vengano arrotondati a quantità inesatte sia che non si perdano centesimi su grandi somme monetarie. Poiché tipi di dati a virgola fissa di MySQL sono stringhe con codifica BCD, questo è esattamente ciò che offrono.

Infine, tieni presente che i linguaggi di programmazione di solito rappresentano valori frazionari utilizzando la virgola mobile binaria tipi:quindi se il tuo database memorizza i valori in un altro formato, devi fare attenzione a come vengono inseriti nella tua applicazione, altrimenti potrebbero essere convertiti (con tutti i problemi che ne derivano) nell'interfaccia.

Quale opzione è la migliore in questo caso?

Spero di averti convinto che i tuoi valori possono tranquillamente (e dovrebbero ) essere archiviato in tipi a virgola mobile senza preoccuparsi troppo di eventuali "imprecisioni"? Ricorda, sono di più precisa di quanto non sia mai stata la tua debole rappresentazione decimale a 3 cifre significative:devi solo ignorare la falsa precisione (ma devi sempre fallo comunque, anche se usi un formato decimale a virgola fissa).

Per quanto riguarda la tua domanda:scegli l'opzione 1 o 2 rispetto all'opzione 3:rende più facili i confronti (ad esempio, per trovare la massa massima, si potrebbe semplicemente usare MAX(mass) , mentre per farlo in modo efficiente su due colonne sarebbe necessario un po' di annidamento).

Tra questi due, non importa quale si scelga:i numeri in virgola mobile vengono memorizzati con un numero costante di bit significativi indipendentemente dalla loro scala .

Inoltre, mentre nel caso generale potrebbe accadere che alcuni valori vengano arrotondati a numeri binari più vicini alla loro rappresentazione decimale originale usando l'opzione 1 mentre contemporaneamente altri vengano arrotondati a numeri binari più vicini alla loro rappresentazione decimale originale usando l'opzione 2, come vedremo tra breve tali errori di rappresentazione manifestarsi solo all'interno della falsa precisione che dovrebbe essere sempre ignorata.

Tuttavia, in questo caso, poiché accade che ci siano 16 once per 1 libbra (e 16 è una potenza di 2), le differenze relative tra i valori decimali originali e i numeri binari memorizzati utilizzando i due approcci sono identiche :

  1. 5.387510 (non 5.3367187510 come indicato nella tua domanda) verrebbe archiviato in un float binary32 come 101.0110001100110011001102 (che è 5.3874998092651367187510 ):questo è 0.0000036% dal valore originale (ma, come discusso sopra, il "valore originale" era già una rappresentazione piuttosto scadente della quantità fisica che rappresenta).

    Sapendo che un float binary32 memorizza solo 7 cifre decimali di precisione, il nostro compilatore sa per certo che tutto dall'ottava cifra in poi è decisamente falsa precisione e quindi deve essere ignorato in ogni caso, quindi a condizione che il nostro valore di input non richiedesse una precisione maggiore di quella (e se così fosse, binary32 era ovviamente la scelta sbagliata del formato), questo garantisce un ritorno a un valore decimale che ha lo stesso aspetto di quello da cui siamo partiti:5.38750010 . Tuttavia, dovremmo davvero applicare la conoscenza del dominio a questo punto (come dovremmo con qualsiasi formato di archiviazione) per scartare qualsiasi ulteriore falsa precisione che potrebbe esistere, come quei due zeri finali.

  2. 86.210 verrebbe memorizzato in un float binary32 come 1010110.001100110011001102 (che è 86.199996948242187510 ):anche questo è 0.0000036% dal valore originario. Come prima, ignoriamo la falsa precisione per tornare al nostro input originale.

Nota come le rappresentazioni binarie dei numeri sono identiche, fatta eccezione per il posizionamento del punto radix (che è a quattro bit di distanza):

101.0110 00110011001100110
101 0110.00110011001100110

Questo perché 5,3875 × 2 =86,2.