为什么FLOAT给我的结果比DECIMAL更准确?

Dua*_*nne 6 t-sql database sql-server relational-database

我正在寻找非常准确的除法结果。

该SQL返回以下结果:

SELECT (CAST(297282.26  AS DECIMAL(38, 30)) / CAST(495470.44 AS DECIMAL(38, 30))) AS ResultDecimal
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

SELECT (CAST(297282.26 AS FLOAT) / CAST(495470.44 AS FLOAT)) AS ResultFloat
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这是WolframAlpha的准确结果: 在此处输入图片说明 http://www.wolframalpha.com/input/?i=297282.26%2F495470.44

我的印象是DECIMAL比FLOAT更准确:

“由于浮点数和实数数据类型的近似性质,当需要精确的数字行为时(例如,在金融应用程序中,涉及舍入的操作或相等性检查中),请不要使用这些数据类型。而应使用整数,十进制,金钱或小金钱数据类型。”

https://technet.microsoft.com/zh-CN/library/ms187912(v=sql.105).aspx

为什么FLOAT计算给我的结果比使用DECIMAL时更准确?

Stu*_*son 5

希望您已经明白,仅仅因为该FLOAT版本在小数点后显示了更多数字,并不一定意味着这些是真实的数字。这是关于精度,而不是准确度

导致这种精度损失的是函数本身,而不是和数据类型CAST之间的差异。FLOATDECIMAL

为了证明这一点,请将您之前的结果与此结果进行比较:

SELECT 297282.26  / 495470.44  AS ResultNoCast
Run Code Online (Sandbox Code Playgroud)

不使用 CAST 函数的结果

在我的查询版本中,文字数字中小数点的存在告诉 SQL Server 将值视为DECIMAL数据类型,并具有由服务器确定的适当长度和精度。结果比您CAST明确地执行时更精确DECIMAL

可以在该函数的官方文档中的截断和舍入结果CAST下找到隐藏的原因线索:

当您转换小数位不同的数据类型时,有时结果值会被截断,有时会被四舍五入。下表显示了该行为。

From     | To       | Behavior
numeric  | numeric  | Round
Run Code Online (Sandbox Code Playgroud)

因此,每个单独的文字值在传入时都被视为 a NUMERIC(与 相同DECIMAL),并且被强制转换为NUMERIC,这一事实会导致舍入。

稍微预测一下你的下一个问题,如果你想从NUMERIC/DECIMAL数据类型得到更精确的结果,你只需要告诉 SQL Server 计算的每个组成部分都更精确:

SELECT 297282.26000000  / 495470.44000000  AS ResultSuperPrecise
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

这似乎(通过实验)是我能得到的最精确的结果:在分子或分母中添加或删除 0 都会使结果不太精确。我无法解释为什么会这样,因为结果只有小数点右边 23 位。


Ste*_*ord 3

我发现使用以下方法时的最佳精度:

SELECT (CAST(297282.26  AS DECIMAL(15, 9)) / CAST(495470.44 AS DECIMAL(24, 2))) AS ResultDecimal
Run Code Online (Sandbox Code Playgroud)

这给出了结果

0.599999991926864496699338915153

我认为实际值(100位)是:

0.5999999919268644966993389151530412187657451370862810705720405842980259326873264124495499670979362562...

请记住 SQL Server 将除法的最大精度和小数位数定义为:

最大精度 = (p1 - s1 + s2) + MAX(6, s1 + p2 + 1) -- 最多 38

最大比例 = MAX(6, s1 + p2 + 1)

其中 p1 和 p2 是两个数字的精度,s1 和 s2 是数字的小数位数。

在这种情况下,最大精度为 (15-9+2) + MAX(6, 9+24+1) = 8 + 34 = 42。

但是 SQL Server 只允许最大精度 38。

最大刻度 = MAX(6, 9+24+1) = 34