SQL Server:Berechnung mit numerischen Literalen
-
21-08-2019 - |
Frage
Ich habe einige Tests mit Gleitkommaberechnungen durchgeführt, um den Präzisionsverlust zu minimieren.Ich bin auf ein Phänomen gestoßen, das ich hier zeigen und hoffentlich eine Erklärung bekommen möchte.
Wenn ich schreibe
print 1.0 / (1.0 / 60.0)
Das Ergebnis ist
60.0024000960
Wenn ich dieselbe Formel schreibe und explizit umwandele float
print cast(1.0 as float) / (cast(1.0 as float) / cast(60.0 as float))
Das Ergebnis ist
60
Bisher dachte ich, dass numerische Literale mit Nachkommastellen automatisch behandelt werden als float
Werte mit der entsprechenden Präzision.Casting zu real
zeigt das gleiche Ergebnis wie beim Casting float
.
- Gibt es eine Dokumentation darüber, wie SQL Server numerische Literale auswertet?
- Von welchem Datentyp sind diese Literale?
- Muss ich sie wirklich besetzen?
float
bessere Präzision erzielen (was für mich ironisch klingt :)? - Gibt es einen einfacheren Weg, als meine Formeln mit Abgüssen zu überladen?
Lösung
SQL Server verwendet die kleinstmögliche Datentyp.
Wenn Sie dieses Skript ausführen
SELECT SQL_VARIANT_PROPERTY(1.0, 'BaseType')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Precision')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Scale')
SELECT SQL_VARIANT_PROPERTY(1.0, 'TotalBytes')
Sie werden sehen, dass SQL Server implizit einen numerischen verwendet (2, 1) Datentyp.
Die Division durch 60.0 setzt das Ergebnis in ZIFFERN (8, 6).
Die endgültige Berechnung setzt das Ergebnis in ZIFFERN (17, 10).
Bearbeiten
Genommen von SQL Server-Online Datentypkonvertierung
In Transact-SQL-Anweisungen, eine Konstante mit einem Dezimalpunkt automatisch umgewandelt in einen Wert um numerische Daten, unter Verwendung der minimale Genauigkeit und Skalierung notwendig. Zum Beispiel ist die Konstante 12.345 wird mit einer Genauigkeit von 5 in einen numerischen Wert umgewandelt und einem Skala von 3.
Andere Tipps
Ja, Sie müssen häufig warf sie eine bessere Präzision zu schweben zu bekommen. Mein Nehmen auf sie:
Für eine bessere Präzision werfen Dezimalstellen vor Berechnungen
Ich denke, es sollte verstanden werden, was sich hinter den Kulissen abspielt, damit man in ähnlichen Fällen später darauf zurückgreifen kann.
Literale numerische Werte mit Dezimalpunkt ohne wissenschaftliche Notation stellen den Datentyp Decimal dar, der als kleinstmöglicher Decimal-Typ gespeichert wird.Gleiches Zitat wie bei Lieven Keersmaekers aus:https://msdn.microsoft.com/en-us/library/ms191530%28SQL.90%29.aspx#_decimal
In Transact-SQL-Anweisungen wird eine Konstante mit einem Dezimalpunkt automatisch in einen numerischen Datenwert konvertiert, wobei die erforderliche minimale Genauigkeit und Skala verwendet werden.Beispielsweise wird die Konstante 12.345 in einen numerischen Wert mit einer Genauigkeit von 5 und einer Skala von 3 umgewandelt.
Die nachgestellten Nullen rechts vom Dezimalpunkt geben den Maßstab an.Die führenden Nullen links vom Dezimalpunkt werden ignoriert.
Einige Beispiele:
1.0 -> Decimal(2,1)
60.0 -> Decimal(3,1)
1.00 -> Decimal(3,2)
01.0 -> Decimal (2,1)
Ein weiterer zu berücksichtigender Punkt ist Vorrang des Datentyps.Wenn ein Operator zwei Ausdrücke unterschiedlicher Datentypen kombiniert, legen die Regeln für die Datentyppriorität fest, dass der Datentyp mit der niedrigeren Priorität in den Datentyp mit der höheren Priorität konvertiert wird.Und noch ein weiterer zu berücksichtigender Punkt: Wenn wir arithmetische Operationen an Decimal-Typen durchführen, ist der resultierende Decimal-Typ, d. h.Präzision und Skalierung hängen sowohl von den Operanden als auch von der Operation selbst ab.Dies ist im Dokument beschrieben Präzision, Maßstab und Länge.
Also ein Teil Ihres Ausdrucks in Klammern
( 1.0 / 60.0 ) is evaluated to 0.016666 and the resulting type is Decimal (8,6)
unter Verwendung der oben genannten Regeln zur Genauigkeit und Skalierung von Dezimalausdrücken.Darüber hinaus wird die Bankrundung bzw. Rundung auf eine gerade Zahl verwendet.Es ist wichtig zu beachten, dass für den Typ „Dezimal“ und „Float“ unterschiedliche Rundungen verwendet werden.Wenn wir den Ausdruck fortsetzen
1.0 / 0.016666 is evaluated to 60.002400096 and the resulting type is Decimal (17,10)
Ein Teil der Diskrepanz ist also darauf zurückzuführen, dass für Dezimaltypen eine andere Rundung verwendet wird als für Float.
Gemäß den oben genannten Regeln würde es ausreichen, nur eine Umwandlung in Klammern zu verwenden.Jedes zweite Literal wird gemäß den Datentyp-Prioritätsregeln auf Float heraufgestuft.
1.0 / (1.0 / cast(60.0 as float))
Und noch etwas WICHTIGES.Selbst dieser Float-Ausdruck berechnet kein genaues Ergebnis.Es ist nur so, dass das Frontend (SSMS oder was auch immer) den Wert auf (ich schätze) Genauigkeit von 6 Stellen rundet und dann nachgestellte Nullen abschneidet.Also d.h.Aus 1,000001 wird 1.
Ganz einfach, nicht wahr?
einen konstanten Schwimmer Ausdruck zu schreiben, versuchen Sie die wissenschaftliche Notation zu verwenden:
select (1.0E0 / (1.0E0 / 60.0E0))
Das Ergebnis ist 60.