SQL 服务器浮点数不稳定

SQL Server float unstable last digits

在 SQL 服务器中,我创建了一个 SQL 数据类型浮点的聚合列(我添加的其他列的组合,多个,求和等)。

但是,当我多次运行相同的查询时,我的浮点数的最后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 对于第二个语句,所以它似乎确实进行了舍入而不是 t运行cation.

我还注意到 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 时添加到最后。即使t运行cating也无济于事,然后添加其他随机数。

似乎 python 在传输过程中以随机方式同时修改 float 和 Decimal,或者 sql 已经或两者都存在不稳定性。

我试过 运行 像这样在 python 一侧设置 np.float64:

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

聚合的处理顺序未定义,就像任何查询结果的顺序未定义一样,除非您使用 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))