【问题标题】:SQL Server float unstable last digitsSQL Server 浮点数不稳定的最后一位数字
【发布时间】:2019-01-06 01:26:39
【问题描述】:

在 SQL Server 中,我创建了一个聚合列(我添加的其他列、多个列、总和等的组合),它是 SQL 数据类型 float。

但是,当我多次运行相同的查询时,我的浮点数的最后 2 位数字不稳定并不断变化。

在我得到随机最后两位数字的浮点数下方 - 我尝试转换为十进制,然后去掉最后两位数字。

select round(convert(decimal(20,19), 0.0020042890676442646), 17,1)
select round(convert(decimal(20,19), 0.0020042890676442654), 17,1)

在 SSMS 中,两者的结果都是:0.0020042890676442600 符合预期。

请注意,这里的输入常量是我从 python 中获取的,所以它们可能已经被修改过。我不能直接从 sql 中获取它们,因为计算异常非常罕见,而且我不知道如何重现它。

但是通过 pypyodbc 到 python 运行这个,有时结果是 python decimal.Decimal 类型,第二个语句的值为 0.0020042890676442700,所以它似乎做舍入而不是截断。

我还注意到 sql 中的计算结果并不总是相同,并且浮点数的最后一位存在不稳定 - 但不知道如何系统地测试它。

转换为浮点数的常量给出:

select convert(float,0.0020042890676442646)
select convert(float,0.0020042890676442654)

结果:0.00200428906764427

用小数括起来并四舍五入:

select round(convert(decimal(20,19), convert(float,0.0020042890676442646)), 17,1)
select round(convert(decimal(20,19), convert(float,0.0020042890676442654)), 17,1)

SSMS 中的结果是:0.0020042890676442700 在这两种情况下。

我尝试直接发回浮点数而不是转换为十进制,但似乎两个不稳定的数字总是在到达 python 时添加到末尾。即使截断也无济于事,然后会添加其他随机数。

似乎python在传输过程中以随机方式同时修改了浮点数和小数点,或者不稳定性已经存在于sql中或两者兼而有之。

我尝试像这样在 python 端截断 np.float64:Truncating decimal digits numpy array of floats

但由于 sql 中的最后一个浮点数可以在 e15 和 e19 之间,我无法设置一致的截断级别,除非我将所有内容都设置为 e15。

【问题讨论】:

  • 。 .我认为您的代码中没有任何浮点数。我认为,常量被解释为小数。
  • 您可以看到使用sp_describe_first_result_set SQL Server 将这两个文字解释为numeric(19,19):EXEC sp_describe_first_result_set N'SELECT 0.0020042890676442646'; EXEC sp_describe_first_result_set N'SELECT 0.0020042890676442654';。看来python在你背后做点什么。
  • 第一个问题是,为什么聚合在不同的时间会给出不同的答案?在计算聚合时,不能保证每次都以相同的顺序处理记录,就像在任何查询中都不能保证顺序一样,除非您使用 ORDER BY。在浮点数上进行数学运算时,顺序可能很重要。如果您有一个浮点数列表并从最小值开始添加,则与从最大值开始添加时会得到不同的答案。
  • 浮点数在 SQL Server 中只能精确到 15 位。如果你有更多,精度将会丢失。这是记录在案的。
  • @KaiAeberli - 小数点分隔符后有 17 位数字,但前两个零不是 有效数字,它们只是浮点数缩放(指数)的结果值:2.00428906764426E-3

标签: sql sql-server python-3.x floating-accuracy pypyodbc


【解决方案1】:

聚合的处理顺序未定义,就像任何查询结果的顺序未定义一样,除非您使用ORDER BY 子句。对于floats,订单很重要。可以使用OVER 子句强制聚合处理的顺序。下面是一些代码来演示:

 -- demonstrate that order matters when adding floats

declare @a float
declare @b float
declare @c float
declare @d float
declare @e float

set @a = 1
set @b = 1
set @c = 9024055778268167

-- add A to B, and then add C
-- result is 9024055778268170

set @d = @a + @b

set @e = @d + @c

select cast( @e as decimal(38,0) )

-- add C to B, and then add A
-- result is 9024055778268168

set @d = @c + @b

set @e = @d + @a

select cast( @e as decimal(38,0) )

-- put these values into a table

create table OrderMatters ( x float )

insert into OrderMatters ( x ) values ( @a )
insert into OrderMatters ( x ) values ( @b )
insert into OrderMatters ( x ) values ( @c )

declare @x float

-- add them in ascending order
-- result is 9024055778268170

select @x = sum(x) over (order by x asc ) from OrderMatters

select cast(@x as decimal(38,0))

-- add them in descending order
-- result is 9024055778268168

select @x = sum(x) over (order by x desc ) from OrderMatters

select cast(@x as decimal(38,0))

【讨论】:

  • 这回答了我的 python 代码的一个问题,我已将 a+b 重构为 b+a 因此不精确。其他问题是:计算 SQL 中的浮点数,而这些浮点数被严重转换为 python。我最终只是通过 ODBC 传输整数并在 python 中计算浮点数,因此无需从 sql float 转换为 np.float64,这似乎造成了不精确。
猜你喜欢
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-28
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多