Warum ist 199.96 - 0 = 200 in SQL?

Habe ich einige clients, die sich seltsam Rechnungen. Ich war in der Lage zu isolieren den Kern des Problems:

SELECT 199.96 - (0.0 * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * CAST(199.96 AS DECIMAL(19, 4)))) -- 200 what the?
SELECT 199.96 - (0.0 * FLOOR(1.0 * CAST(199.96 AS DECIMAL(19, 4)))) -- 199.96
SELECT 199.96 - (0.0 * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * 199.96)) -- 199.96

SELECT 199.96 - (CAST(0.0 AS DECIMAL(19, 4)) * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * CAST(199.96 AS DECIMAL(19, 4)))) -- 199.96
SELECT 199.96 - (CAST(0.0 AS DECIMAL(19, 4)) * FLOOR(1.0 * CAST(199.96 AS DECIMAL(19, 4))))                         -- 199.96
SELECT 199.96 - (CAST(0.0 AS DECIMAL(19, 4)) * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * 199.96))                         -- 199.96

-- It gets weirder...
SELECT (0 * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * CAST(199.96 AS DECIMAL(19, 4)))) -- 0
SELECT (0 * FLOOR(1.0 * CAST(199.96 AS DECIMAL(19, 4))))                         -- 0
SELECT (0 * FLOOR(CAST(1.0 AS DECIMAL(19, 4)) * 199.96))                         -- 0

-- so... ... 199.06 - 0 equals 200... ... right???
SELECT 199.96 - 0 -- 199.96 ...NO....

Hat jemand eine Ahnung, was zum Teufel ist hier passiert? Ich meine, es hat sicher etwas zu tun mit dem decimal-Datentyp, aber ich kann nicht wirklich wickeln Sie meinen Kopf herum...


Gab es eine Menge Verwirrung darüber, von welchen Datentyp die Anzahl Literale wurden, so entschied ich mich um zu zeigen das wahre line:

PS.SharePrice - (CAST((@InstallmentCount - 1) AS DECIMAL(19, 4)) * CAST(FLOOR(@InstallmentPercent * PS.SharePrice) AS DECIMAL(19, 4))))

PS.SharePrice DECIMAL(19, 4)

@InstallmentCount INT

@InstallmentPercent DECIMAL(19, 4)

Machte ich sicher, dass das Ergebnis jeder operation mit einem Operanden eines anderen Typs als DECIMAL(19, 4) gewirkt wird ausdrücklich vor der Anwendung, um den äußeren Kontext.

Dennoch bleibt das Ergebnis 200.00.


Habe ich jetzt erstellt ein eingekocht Probe, die Sie Jungs können ausführen auf Ihrem computer.

DECLARE @InstallmentIndex INT = 1
DECLARE @InstallmentCount INT = 1
DECLARE @InstallmentPercent DECIMAL(19, 4) = 1.0
DECLARE @PS TABLE (SharePrice DECIMAL(19, 4))
INSERT INTO @PS (SharePrice) VALUES (599.96)

-- 2000
SELECT
  IIF(@InstallmentIndex < @InstallmentCount,
  FLOOR(@InstallmentPercent * PS.SharePrice),
  1999.96)
FROM @PS PS

-- 2000
SELECT
  IIF(@InstallmentIndex < @InstallmentCount,
  FLOOR(@InstallmentPercent * CAST(599.96 AS DECIMAL(19, 4))),
  1999.96)
FROM @PS PS

-- 1996.96
SELECT
  IIF(@InstallmentIndex < @InstallmentCount,
  FLOOR(@InstallmentPercent * 599.96),
  1999.96)
FROM @PS PS

-- Funny enough - with this sample explicitly converting EVERYTHING to DECIMAL(19, 4) - it still doesn't work...
-- 2000
SELECT
  IIF(@InstallmentIndex < @InstallmentCount,
  FLOOR(@InstallmentPercent * CAST(199.96 AS DECIMAL(19, 4))),
  CAST(1999.96 AS DECIMAL(19, 4)))
FROM @PS PS

Jetzt habe ich etwas...

-- 2000
SELECT
  IIF(1 = 2,
  FLOOR(CAST(1.0 AS decimal(19, 4)) * CAST(199.96 AS DECIMAL(19, 4))),
  CAST(1999.96 AS DECIMAL(19, 4)))

-- 1999.9600
SELECT
  IIF(1 = 2,
  CAST(FLOOR(CAST(1.0 AS decimal(19, 4)) * CAST(199.96 AS DECIMAL(19, 4))) AS INT),
  CAST(1999.96 AS DECIMAL(19, 4)))

Was solls - Etage soll eine Ganzzahl zurück sowieso. Was ist denn hier Los? 😀


Ich denke, dass ich es nun geschafft wirklich Kochen es nach unten, um die Wesen 😀

-- 1.96
SELECT IIF(1 = 2,
  CAST(1.0 AS DECIMAL (36, 0)),
  CAST(1.96 AS DECIMAL(19, 4))
)

-- 2.0
SELECT IIF(1 = 2,
  CAST(1.0 AS DECIMAL (37, 0)),
  CAST(1.96 AS DECIMAL(19, 4))
)

-- 2
SELECT IIF(1 = 2,
  CAST(1.0 AS DECIMAL (38, 0)),
  CAST(1.96 AS DECIMAL(19, 4))
)
  • 199.96 -0 nicht gleich 200. Alle diejenigen wirft, und die Böden mit impliziten Konvertierungen, die floating-point und zurück sind doch garantiert in der Folge der Präzision Verlust.
  • was ist der Typ von 199.96 durch die Art und Weise? Es ist nicht eine Dezimalzahl. Es konvertiert implizit einen Typ, der kompatibel mit den anderen Operanden
  • 199.96 hat der Typ decimal(19, 4) in der realen Probe.
  • nur, wenn es kam aus einer Tabelle. Als literal in einem Ausdruck ist es wahrscheinlich eine float
  • Die Antwort von Tapakah Ua (mit meinen änderungen) erklärt, warum das neue sample nicht funktioniert, selbst wenn alles Stimmen, um decimal(19,4). Sql Server mutieren die Typen während der Arithmetik, und in diesem Fall das Ergebnis von der Art der mutation die überläufe der größten Art Sql-Server unterstützt, welche Kräfte es zu entfernen, die Waage. So dass man am Ende bis Rundung die 199.96 Wert.
  • Oh... und Floor() hat nicht kehren Sie ein int. Es gibt die gleiche Art wie die ursprüngliche Ausdruck, mit dem dezimalen Teil entfernt. Für den rest, der IIF() Funktion führt die Art mit der höchsten Priorität (docs.microsoft.com/en-us/sql/t-sql/functions/...). Also das 2. Beispiel, wo Sie cast zu int, die höhere Priorität ist die einfache Besetzung als numeric(19,4).
  • Tolle Antwort (wer wusste, Sie könnte prüfen, Metadaten eines sql-Variante?) aber ich 2012 die erwarteten Ergebnisse erhalten (199.96).
  • MS SQL Server und Sybase beide nennen Ihre Sprache T-SQL-denn an einer Stelle waren Sie arbeiten zusammen auf der gleichen Basis-Produkt. Siehe diese MS blog-post für details.
  • Ich bin nicht allzu vertraut mit MS SQL, aber ich muss sagen, dass der Blick auf all diese cast-Operationen und so weiter schnell meine Aufmerksamkeit gefangen.. also ich muss link, denn niemand sollte jemals mit floating-point-Typen auf die Währung.
  • Nicht verwenden, floating-point-zahlen, mit Geld umzugehen.
  • Und für diese Angelegenheit, vermeiden Sie SQL Server, wenn möglich 🙂
  • Keine floating-point-Datentypen verwendet, die in die Frage. Und ich denke, dass keine floating-point-operation verwendet wird, in jedem Beispiel.
  • Sind nicht die 1.0s angefangen als floating-point-zahlen, bevor die as decimal(...) Teil?
  • ein 1.0 ist eine dezimale Konstante 1e0 ist eine float-Konstante.

InformationsquelleAutor Silverdust | 2018-07-20
Schreibe einen Kommentar