【问题标题】:Strange behavior while rounding in SQL server 2008在 SQL Server 2008 中舍入时出现奇怪的行为
【发布时间】:2013-11-21 13:49:41
【问题描述】:

在某个时候,我有一个 numeric(28,10) 并且我将它投入到 money 中(我知道这很糟糕,但由于遗留原因,我必须返回 money)同时我还必须设置符号(乘以 +1/-1)。

在第一次尝试中,我将 +/-1 转换为匹配 numeric 类型。

对于值133.3481497944,我们遇到了一个奇怪的行为(我已经简化了实际代码,以便只保留演示问题所需的元素)

SELECT CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) AS money)

133.3482

没有正确四舍五入...

去掉演员表解决问题

SELECT CAST(CAST(133.3481497944 AS numeric(28,10)) * 1 AS money)

133.3481

有人知道 SQL 中发生了什么吗?乘以 1 和 cast(1 AS numeric(28,10)) 如何影响舍入结果?

【问题讨论】:

  • 我认为 1 是一个红鲱鱼:SELECT Cast(Cast(133.3481497944 As numeric(28,10)) As money) 返回133.3481。这表明数字算术是问题所在。
  • 是的...SELECT Cast(133.3481497944 As numeric(28,10)) * Cast(1 As numeric(28,10)) 返回133.348150,因此它被四舍五入。
  • 如果你想要钱,为什么要先转换成数字再转换成钱? SELECT Cast(CAST(133.3481497944 AS money) * cast(1 as money) as money) 给出 133.3481

标签: sql sql-server casting rounding


【解决方案1】:

当乘以数字时,SQL 使用以下规则来确定输出的精度和小数位数:

p = p1 + p2 + 1
s = s1 + s2

这是有道理的 - 您不希望 1.5 * 2.5 被截断为小数点后一位。您也不希望 101 * 201 被限制为 3 位精度,给您 20300 而不是 20301。

在您的情况下,这将导致精度为 57 和比例为 20,这是不可能的 - 最大精度和比例为 38。

如果结果类型太大,则会牺牲十进制数字以保留结果的整数(最高有效)部分。

来自SQL Programmability & API Development Team Blog

在 SQL Server 2005 RTM(和以前的版本)中,我们决定在乘法和除法中保留 6 的最小小数位数。

所以 您的 答案取决于您需要的乘数有多精确。为了保留 10 位小数精度。如果乘数需要大于 9 的比例,那么十进制数字可能会被截断。如果你使用更小的精度和规模,你应该没问题:

SELECT CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(9,7)) AS money)

产生133.3481

【讨论】:

  • 很棒的答案,+1!
【解决方案2】:

我在这里没有看到任何ROUNDing。我只看到铸造。当您CAST 时,不要假设它会四舍五入。从历史上看,当我们强制转换环境时(无论是否 SQL 服务器)或行为不符合我们的预期 - 尤其是当我们谈论 FLOAT 时。

【讨论】:

    【解决方案3】:
    SELECT 
    CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) AS money) --Your original,
    CAST(1 AS numeric(28,10)) --Just the 1 casted,
    CAST(133.3481497944 AS numeric(28,10)) --Your expected calculation,
    CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) -- The actual calculation
    
    SELECT
    CAST(133.3481497944 AS numeric(28,10))*cast(1.5 AS numeric(28,10)),
    CAST(133.3481497944 AS numeric(28,10))*1.5,
    CAST((133.3481497944*1) AS money),
    133.3481497944*1
    

    返回

    133.3482    
    1.0000000000    
    133.3481497944  
    133.348150
    
    
    200.022225  
    200.02222469160
    
    
    133.3481    
    133.3481497944
    

    因此,如上所述,实际上并没有任何真正的舍入,而是在投射过程中损失了精度。至于具体为什么,我不知道。很可能在计算(乘法)期间使用 Numeric(28,10) 时会降低一些精度。

    我添加了第二行以表明您实际上可能不需要数字转换。

    【讨论】:

    • 乘法中丢失的精度是导致最终结果出现意外舍入的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 2013-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多