T-SQL 十进制除法精度

T-SQL Decimal Division Accuracy

提问人:MT. 提问时间:1/8/2009 最后编辑:ChanduMT. 更新时间:5/19/2021 访问量:56955

问:

有谁知道为什么,使用 SQLServer 2005

SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,9),12499999.9999)

给我 11.74438969709659,

但是当我将分母上的小数位增加到 15 时,我得到的答案不太准确:

SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,15),12499999.9999)

给我 11.74438969

T-SQL 精度

评论

2赞 Ian G 1/8/2009
顺便说一句,Windows 计算器给出了 11.744389697096595117576772760941

答:

4赞 cmsjr 1/8/2009 #1

转换表达式而不是参数。

select  CONVERT(DECIMAL(38,36),146804871.212533 / 12499999.9999)
38赞 gbn 1/8/2009 #2

对于乘法,我们只需将每个参数中的小数位数相加(使用笔和纸)即可计算出输出十进制位数。

但分裂只会让你大吃一惊。我现在要躺下了。

不过,在SQL方面,它完全符合预期。

--Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
--Scale = max(6, s1 + p2 + 1)

--Scale = 15 + 38 + 1 = 54
--Precision = 30 - 15 + 9 + 54 = 72
--Max P = 38, P & S are linked, so (72,54) -> (38,20)
--So, we have 38,20 output (but we don use 20 d.p. for this sum) = 11.74438969709659
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,9),12499999.9999)


--Scale = 15 + 38 + 1 = 54
--Precision = 30 - 15 + 15 + 54 = 84
--Max P = 38, P & S are linked, so (84,54) -> (38,8)
--So, we have 38,8 output = 11.74438969
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,15),12499999.9999)

如果您也遵循此规则,如果您将每个数字对视为

  • 146804871.212533000000000 和 12499999.999900000
  • 146804871.212533000000000 和 12499999.999900000000000

评论

0赞 MT. 1/8/2009
谢谢。老实说,我为我的特定问题使用了 FLOAT(因为我不需要 100% 准确),但我对使用小数时发生的事情感到困惑。
2赞 Veysel Ozdemir 3/20/2016
不幸的是,SQL Server 在算术:(方面非常糟糕因此,还值得一提的是,如果需要在 t-sql 中进行乘法和除法的准确结果,最好的选择是: 定义和使用 CLR 函数。请参阅:skylinetechnologies.com/Insights/Skyline-Blog/March-2013/...,了解一个好的介绍
19赞 Vedran Kesegic 4/3/2009 #3

简而言之,使用 DECIMAL(25,13),您可以完成所有计算 - 您将获得声明的精度:小数点前 12 位,后 13 位小数位。 规则是:p+s 必须等于 38,您将处于安全状态! 为什么会这样? 因为 SQL Server 中算术的实现非常糟糕! 在他们修复它之前,请遵循该规则。

评论

0赞 MrEdmundo 11/6/2013
是否有任何参考资料可以说明为什么这对所有计算都是“好的”?
1赞 Triynko 3/27/2017
select cast(1000000 as DECIMAL(38,19)) / cast(9223372036854775807 as DECIMAL(38,19));<< OK << 算术溢出select cast(1000000 as DECIMAL(25,13)) / cast(9223372036854775807 as DECIMAL(25,13));
18赞 sonal 12/7/2009 #4

我注意到,如果你将除数值转换为浮点数,它会给你正确的答案,即:

select 49/30                   (result = 1)

将变成:

select 49/cast(30 as float)    (result = 1.63333333333333)

评论

0赞 Simon P Stevens 2/27/2020
值得注意的是,您应该小心这种类型的演员,不要盲目使用。小数和浮点数以不同的方式存储数字表示形式,浮点数具有一些不直观的影响,您在执行此操作时应注意这些效果。例如: select cast(cast(0.1 as float) as decimal(38,18)) 给出输出:0.1000000000000000006 这是因为 0.1 不能用二进制准确表示,所以它存储的东西稍微偏离了。当您开始乘法或四舍五入时,这可能会产生影响,因为结果可能只是略微偏离。
6赞 Campey 10/8/2010 #5

我们对神奇的转变感到困惑,

P&S是有联系的,所以:

  1. (72,54) -> (38,29)
  1. (84,54) -> (38,8)

假设是错别字,应该是,以下是数学:(38,29)(38,20)

  1. i. 72 - 38 = 34, ii. 54 - 34 = 20

  2. i. 84 - 38 = 46, ii. 54 - 46 = 8

这就是推理:

i. 输出精度减去最大精度是我们将要丢弃的数字。

ii. 然后输出比例减去我们将要扔掉的东西给我们......输出刻度中的剩余数字。

希望这能帮助其他任何试图理解这一点的人。

评论

1赞 George Joseph 2/24/2021
错别字(应该是 i. 84 - 38(不是 58) = 46,ii. 54 - 46 = 8)。这是非常有用@Campey
0赞 Campey 9/16/2021
谢谢@GeorgeJoseph,我在 5 月份就纳入了您的更正,但似乎忘记了我的谢谢!
2赞 morex 3/16/2017 #6

使用以下方法可能会有所帮助:

SELECT COL1 * 1.0 / COL2

评论

0赞 default locale 3/16/2017
你为什么这么认为?