Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Best Practice zum Speichern von Gewichten in einer SQL-Datenbank?

Sie behaupten, dass Fließkommazahlen inhärente Ungenauigkeiten enthalten. Ich denke, dass dies es verdient, zuerst ein wenig untersucht zu werden.

Bei der Entscheidung für ein Zahlensystem um eine Zahl darzustellen (ob auf einem Stück Papier, in einem Computerschaltkreis oder anderswo), gibt es zwei getrennte Probleme zu berücksichtigen:

  1. seine Basis; und

  2. sein Format .

Wähle eine Basis, irgendeine Basis …

Durch endlichen Raum begrenzt, kann man kein beliebiges Mitglied einer unendlichen Menge darstellen . Zum Beispiel:Egal wie viel Papier Sie kaufen oder wie klein Ihre Handschrift ist, es wäre immer möglich, eine Ganzzahl zu finden, die nicht in den vorgegebenen Platz passt (Sie könnten einfach weitere Ziffern anhängen, bis das Papier aufgebraucht ist). Also mit Ganzzahlen , beschränken wir unseren endlichen Raum normalerweise darauf, nur diejenigen darzustellen, die in ein bestimmtes Intervall fallen – z. wenn wir Platz für das positive/negative Vorzeichen und drei Ziffern haben, könnten wir uns auf das Intervall [-999,+999] beschränken .

Alle nicht leeres Intervall enthält eine unendliche Menge reeller Zahlen. Mit anderen Worten, egal in welchem ​​Intervall man die reellen Zahlen übernimmt – sei es [-999,+999] , [0,1][code> , [0.000001,0.000002] oder irgendetwas anderes - es gibt immer noch eine unendliche Menge von Realzahlen innerhalb dieses Intervalls (man muss nur immer wieder (nicht null) Nachkommastellen anhängen)! Daher müssen beliebige reelle Zahlen immer verwendet werden auf etwas "gerundet" werden, das kann im endlichen Raum dargestellt werden.

Die Menge der reellen Zahlen, die im endlichen Raum dargestellt werden können, hängt vom verwendeten Zahlensystem ab. In unserem (bekannten) Positional Basis-10 System reicht endlicher Platz für eine Hälfte (0.510 ), aber nicht für ein Drittel (0.33333 …10 ); im Gegensatz dazu in der (weniger bekannten) Position base-9 System ist es umgekehrt (dieselben Zahlen sind jeweils 0.44444…9 und 0.39 ). Die Folge davon ist, dass einige Zahlen, die nur mit geringem Platzbedarf in der Positionsbasis 10 dargestellt werden können (und daher auftauchen). für uns Menschen sehr "rund" sein), z.B. ein Zehntel, würde eigentlich unendliche binäre Schaltungen erfordern, um genau gespeichert zu werden (und erscheinen daher unseren digitalen Freunden nicht sehr "rund")! Da 2 ein Faktor von 10 ist, gilt dies insbesondere nicht umgekehrt:Jede Zahl, die mit endlicher Binärzahl dargestellt werden kann, kann auch mit endlicher Dezimalzahl dargestellt werden.

Bei kontinuierlichen Mengen geht es nicht besser. Letztendlich müssen solche Größen in einigen eine endliche Darstellung verwenden Zahlensystem:es ist willkürlich, ob dieses System Computerschaltungen, menschlichen Fingern, irgendetwas anderem oder gar nichts leicht macht – welches System auch immer verwendet wird, der Wert muss gerundet und daher immer führt zu "Darstellungsfehler".

Mit anderen Worten, selbst wenn man ein perfekt genaues Messgerät hat (was physikalisch unmöglich ist), dann ist jede Messung, die es meldet, bereits gerundet zu einer Zahl, die zufällig auf die Anzeige passt (in welcher Basis auch immer sie verwendet wird – normalerweise dezimal, aus offensichtlichen Gründen). "86,2 oz" ist also nie wirklich "86,2 oz ", sondern eine Darstellung von "etwas zwischen 86,1500000... oz und 86,2499999... oz ". (Da das Instrument in Wirklichkeit unvollkommen ist, können wir eigentlich nur sagen, dass wir einige Grad Vertrauen dass der tatsächliche Wert in dieses Intervall fällt – aber das weicht definitiv etwas vom Punkt hier ab).

Aber wir können es bei diskreten Mengen besser machen . Solche Werte sind keine "beliebigen reellen Zahlen" und daher gilt nichts von dem Obigen für sie:Sie können exakt dargestellt werden in dem Zahlensystem, in dem sie definiert wurden – und in der Tat sollten (da die Umwandlung in ein anderes Zahlensystem und das Abschneiden auf eine endliche Länge zu einer Rundung auf eine ungenaue Zahl führen würde). Computer können solche Situationen (ineffizient) handhaben, indem sie die Zahl als Zeichenfolge darstellen:z. Betrachten Sie ASCII oder BCD Codierung.

Wenden Sie ein Format an…

Da es eine Eigenschaft der (etwas willkürlichen) Basis des Zahlensystems ist, ob ein Wert "rund" erscheint oder nicht, hat keinen Einfluss auf seine Präzision . Das ist eine wirklich wichtige Beobachtung , was der Intuition vieler Menschen zuwiderläuft (und das ist der Grund, warum ich so viel Zeit damit verbracht habe, die numerische Basis oben zu erklären).

Die Genauigkeit wird stattdessen dadurch bestimmt, wie viele signifikante Zahlen sind eine Vertretung hat . Wir brauchen ein Speicherformat, das in der Lage ist, unsere Werte auf mindestens aufzuzeichnen so viele signifikante Zahlen wie wir sie für richtig halten . Nehmen wir als Beispiel Werte, die wir als richtig erachten, wenn sie als 86.2 angegeben werden und 0.0000862 , die zwei häufigsten Optionen sind:

  • Fixpunkt , wobei die Anzahl signifikanter Stellen von der Größe abhängt :z.B. in einer festen 5-Dezimalkomma-Darstellung würden unsere Werte als 86.20000 gespeichert und 0.00009 (und haben daher 7 bzw. 1 signifikante Genauigkeitsziffern). In diesem Beispiel ist Präzision verloren gegangen im letzteren Wert (und in der Tat würde es nicht viel mehr brauchen, damit wir überhaupt nicht in der Lage gewesen wären, irgendetwas darzustellen von Bedeutung); und der vorherige Wert gespeicherte falsche Genauigkeit , was eine Verschwendung unseres endlichen Speicherplatzes ist (und tatsächlich würde es nicht viel mehr brauchen, damit der Wert so groß wird, dass er die Speicherkapazität überläuft).

    Ein gängiges Beispiel dafür, wann dieses Format geeignet sein könnte, ist ein Buchhaltungssystem:Geldbeträge müssen normalerweise auf den Cent genau verfolgt werden unabhängig von ihrer Größe (daher ist für kleine Werte weniger Genauigkeit und für große Werte mehr Genauigkeit erforderlich). Zufällig wird Währung normalerweise auch als diskret betrachtet (Pennies sind unteilbar), daher ist dies auch ein gutes Beispiel für eine Situation, in der eine bestimmte Basis (dezimal für die meisten modernen Währungen) wünschenswert ist, um die oben diskutierten Darstellungsfehler zu vermeiden.

  • Gleitkomma , wobei die Anzahl signifikanter Stellen unabhängig von der Größe konstant ist :z.B. In einer 5-stelligen Dezimaldarstellung würden unsere Werte als 86.200 gespeichert und 0.000086200 (und per Definition beide Male 5 signifikante Genauigkeitsziffern haben). In diesem Beispiel wurden beide Werte ohne Genauigkeitsverlust gespeichert; und beide haben auch die gleiche Menge von falscher Genauigkeit, was weniger verschwenderisch ist (und wir können daher unseren endlichen Raum nutzen, um einen weitaus größeren Bereich von Werten darzustellen – sowohl große als auch kleine).

    Ein gängiges Beispiel dafür, wann dieses Format angemessen sein könnte, ist die Aufzeichnung aller realen Messungen :die Präzision von Messinstrumenten (die alle unter systematischen leiden und zufällig Fehler) ist unabhängig von der Skalierung ziemlich konstant, sodass bei ausreichend signifikanten Zahlen (normalerweise um 3 oder 4 Stellen) absolut keine Genauigkeit verloren geht, selbst wenn eine Änderung der Basis zu einer Rundung auf eine andere Zahl führte .

    Aber wie genau sind die Fließkomma-Speicherformate von unseren Computern verwendet?

    Das Wichtigste ist, dass diese Formate jeweils über Zehntausend liegen und über eine Billion mal genauer als „86,2“ zu sagen – obwohl exakte Umrechnungen der Binärzahl zurück in die Dezimalzahl fälschlicherweise falsche Genauigkeiten enthalten (die wir ignorieren müssen:mehr dazu in Kürze)!

Beachten Sie auch, dass beides behoben und Fließkommaformate führen zu einem Genauigkeitsverlust, wenn ein Wert genauer bekannt ist, als das Format unterstützt. Solche Rundungsfehler kann sich in arithmetischen Operationen ausbreiten, um scheinbar fehlerhafte Ergebnisse zu liefern (was zweifellos Ihren Hinweis auf die "inhärenten Ungenauigkeiten" von Gleitkommazahlen erklärt):zum Beispiel 3 × 3000 in 5-stelligem Festkomma würde 999.99000 ergeben statt 1000.00000; und 7 − ⁄50 in einer 5-stelligen Gleitkommazahl würde 0.0028600 ergeben statt 0.0028571 .

Das Feld der numerischen Analyse widmet sich dem Verständnis dieser Effekte, aber es ist wichtig zu erkennen, dass alle Ein brauchbares System (selbst das Ausführen von Berechnungen im Kopf) ist anfällig für solche Probleme, da keine Berechnungsmethode, deren Beendigung garantiert ist, jemals unendliche Genauigkeit bieten kann :Denken Sie zum Beispiel daran, wie man die Fläche eines Kreises berechnet – der für π verwendete Wert wird zwangsläufig an Genauigkeit verlieren, was sich auf das Ergebnis auswirkt.

Schlussfolgerung

  1. Reale Messungen sollten binäre Fließkommazahlen verwenden :Es ist schnell, kompakt, extrem präzise und nicht schlechter als alles andere (einschließlich der Dezimalversion, mit der Sie begonnen haben). Seit Gleitkomma-Datentypen von MySQL IEEE754 sind, bieten sie genau das.

  2. Währungsanwendungen sollten den Denar-Festpunkt verwenden :Während es langsam ist und Speicher verschwendet, stellt es sicher, dass Werte nicht auf ungenaue Mengen gerundet werden und dass bei großen Geldsummen kein Cent verloren geht. Seit den Festkomma-Datentypen von MySQL BCD-codierte Strings sind, bieten sie genau das.

Denken Sie schließlich daran, dass Programmiersprachen normalerweise Bruchwerte mit binären Gleitkommazahlen darstellen Typen:Wenn Ihre Datenbank also Werte in einem anderen Format speichert, müssen Sie vorsichtig sein, wie sie in Ihre Anwendung gebracht werden, oder sie werden möglicherweise an der Schnittstelle konvertiert (mit allen daraus resultierenden Problemen).

Welche Option ist in diesem Fall die beste?

Hoffentlich habe ich Sie davon überzeugt, dass Ihre Werte bedenkenlos (und sollten ) in Fließkommatypen gespeichert werden, ohne sich zu viele Gedanken über "Ungenauigkeiten" zu machen? Denken Sie daran, sie sind mehr präziser als Ihre schwache 3-stellige Dezimaldarstellung es jemals war:Sie müssen nur falsche Genauigkeiten ignorieren (aber man muss immer tun Sie dies trotzdem, auch wenn Sie ein Festkomma-Dezimalformat verwenden).

Zu Ihrer Frage:Wählen Sie entweder Option 1 oder 2 gegenüber Option 3 – es erleichtert Vergleiche (um beispielsweise die maximale Masse zu finden, könnte man einfach MAX(mass) verwenden , wohingegen eine effiziente Ausführung über zwei Spalten einige Verschachtelungen erfordern würde).

Zwischen diesen beiden ist es egal, welche man wählt – Gleitkommazahlen werden unabhängig von ihrer Skalierung mit einer konstanten Anzahl signifikanter Bits gespeichert .

Während es im allgemeinen Fall passieren kann, dass einige Werte mit Option 1 auf Binärzahlen gerundet werden, die näher an ihrer ursprünglichen Dezimaldarstellung liegen, werden gleichzeitig andere mit Option 2 auf Binärzahlen gerundet, die näher an ihrer ursprünglichen Dezimaldarstellung liegen, wie wir werden in Kürze sehen, dass sich solche Darstellungsfehler nur innerhalb der falschen Genauigkeit manifestieren, die immer ignoriert werden sollte.

Allerdings in diesem Da es in diesem Fall vorkommt, dass 16 Unzen zu 1 Pfund gehören (und 16 eine Potenz von 2 ist), sind die relativen Unterschiede zwischen ursprünglichen Dezimalwerten und gespeicherten Binärzahlen bei Verwendung der beiden Ansätze identisch :

  1. 5.387510 (nicht 5.3367187510 wie in Ihrer Frage angegeben) würde in einem binären 32-Float als 101.0110001100110011001102 gespeichert (das ist 5.3874998092651367187510 ):Dies ist 0,0000036 % vom ursprünglichen Wert (aber, wie oben besprochen, war der "ursprüngliche Wert" bereits eine ziemlich miese Darstellung der physikalischen Größe, die er darstellt).

    Unser Compiler weiß, dass ein binärer 32-Float nur 7 Dezimalstellen an Genauigkeit speichert, und weiß das mit Sicherheit dass ab der 8. Stelle alles definitiv ist falsche Genauigkeit und daher müssen in alle ignoriert werden Fall – also vorausgesetzt, dass unser Eingabewert nicht mehr Genauigkeit erfordert (und wenn doch, war binary32 offensichtlich das falsche Format), das garantiert eine Rückkehr zu einem Dezimalwert, der genauso rund aussieht wie der, von dem wir ausgegangen sind:5.38750010 . Allerdings sollten wir wirklich Domänenwissen anwenden an dieser Stelle (wie wir es bei jedem Speicherformat tun sollten), um alle weiteren möglicherweise vorhandenen falschen Genauigkeiten zu verwerfen, wie z. B. diese beiden nachgestellten Nullen.

  2. 86.210 würde in einem binären 32-Float als 1010110.001100110011001102 gespeichert werden (das ist 86.199996948242187510 ):Dies ist auch 0,0000036 % vom ursprünglichen Wert. Wie zuvor ignorieren wir dann die falsche Genauigkeit, um zu unserer ursprünglichen Eingabe zurückzukehren.

Beachten Sie, dass die binären Darstellungen der Zahlen identisch sind, mit Ausnahme der Platzierung des Radixpunkts (was vier Bit auseinander liegt):

101.0110 00110011001100110
101 0110.00110011001100110

Denn 5,3875 × 2 =86,2.