【问题标题】:Unexpected results with double casting numeric datatype in SQL ServerSQL Server 中双重转换数字数据类型的意外结果
【发布时间】:2018-07-10 21:48:55
【问题描述】:

我最近在 ETL 过程中遇到了一个奇怪的案例,结果对我来说似乎无法预测。我读过Difference between numeric, float and decimal in SQL Server,但我不认为这是溢出或小数精度问题。

场景:

SQL Server 2008 SP3 中的源表“test”,a 列声明为数字 (38,6)。

结果首先转换为 real,然后转换为 int。如果从 numeric 直接转换为 int,则不会出现此问题。

结果:

SELECT a,CAST(a as real) as real_a,CAST(CAST(a as real) as int) as int_a FROM test;
  • 电话:778881838.810000
  • real_a: 7.78819E+08
  • int_a: 778881856

在 SQL Server 2017 (sql fiddle) 中运行的相同实验给出了以下结果: http://sqlfiddle.com/#!18/45aca/2

a: 778881838.81
real_a: 778881860
int_a: 778881856

我可以(模糊地)理解 ..19E+08 的情况,但是为什么在双重转换的情况下会有 +18 的差异?这个数字对我来说似乎完全是任意的。

【问题讨论】:

    标签: sql sql-server types


    【解决方案1】:

    好的,首先,real_a 在 SQL Server 2017 中的结果是不是 778881860。它是778881856,就像在 SQL Server 2008 中一样。客户端如何呈现这个浮点值是另一回事——Management Studio 向我展示了7.788819E+08sqlcmd 产生7.7888186E+8,显然 SQL Fiddle 完全使用了另一个库(我个人会对此有疑问,因为它会掩盖重要数字!)

    这个值不是任意的。 REAL 是单精度浮点类型,不能准确表示 778881838.81。最接近的可表示值是778881856,因此是您的结果(下一个较低的可表示值是778881792)。无需转换为INT,您可以使用

    查看此值
    SELECT STR(CONVERT(REAL, CONVERT(NUMERIC(38, 6), 778881838.810000)), 40, 16)
    
    778881856.0000000000000000
    

    您对“双精度”一词的使用让我觉得您将此与 FLOAT 混淆了,后者是双精度浮点类型。 FLOAT 也不能准确地表示这个值,但它更接近:

    SELECT STR(CONVERT(FLOAT, CONVERT(NUMERIC(38, 6), 778881838.810000)), 40, 16)
    
    778881838.8099999400000000
    

    将此值转换为INT 会产生(截断的)778881838。 (这种截断是documented,不会发生在转换为NUMERIC 的情况下;如果您更喜欢778881839,则需要先转换为ROUND。)

    【讨论】:

    • 完美。您能否在答案中添加可表示值是如何计算的,以便我可以接受?
    • @pkExec:你的意思是,我们如何从778881838.810000 得到单精度浮点值778881856 作为最接近的值?我必须深入研究 IEEE 754 标准以获取详细信息——我不能自称是专家。如果您在this 之类的网站上输入数字,您将看到转换(和错误)。单击 +- 按钮以向前/向后移动精确值。
    【解决方案2】:

    其他想要在本地测试的人的简单示例:

    DECLARE  @test numeric (38,6)='778881838.810000'
    SELECT @test as [Original],CAST(@test as real) as real_a,CAST(CAST(@test as real) as int) as int_a;
    
    Original            real_a          int_a
    778881838.810000    7.788819E+08    778881856
    

    您可能需要来自 Microsoft 的人员来解释它在 SQL 引擎中的工作方式(当然要知道他们做出这个决定的原因),但我会尝试一下推理:

    如果输出在第一次转换时采用科学计数法,然后需要转换为 int,则它将 int 设置为产生该科学计数法的最小值。它以 6 而不是 5 结尾,因为对 5 的舍入并不总是在所有情况下都四舍五入(例如Alternating tie-breaking)。

    但是,无论出于何种原因,如果精度很重要,则应显式转换为具有已定义精度的数值数据类型。

    【讨论】:

      【解决方案3】:

      当您想从浮点数或实数转换为字符数据时,使用 STR 字符串函数通常比 CAST( ) 更有用。这是因为 STR 可以更好地控制格式。有关详细信息,请参阅 STR (Transact-SQL) 和函数 (Transact-SQL)。

      请找到以下链接

      USE STR Instead of real

      STR example 使用以下查询:-

      SELECT a,STR(a ,38,6) as real_a,CAST(CAST(a as real) as int) as int_a FROM test;
      

      如果您发现任何问题,请告诉我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-26
        • 1970-01-01
        • 2021-05-19
        • 1970-01-01
        • 2018-07-09
        • 1970-01-01
        • 2019-06-03
        • 2016-07-28
        相关资源
        最近更新 更多