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
子句。在 float
s 的情况下,顺序很重要。可以使用 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))
在 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
子句。在 float
s 的情况下,顺序很重要。可以使用 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))