浮点数的不确定性和


10

让我指出一个明显的拳头:我完全理解浮点类型不能准确表示十进制值。这不是这个!但是,浮点计算应该是确定性的

既然已经解决了,让我向您展示我今天观察到的奇怪案例。我有一个浮点值列表,我想总结一下:

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT STR(SUM(#someFloats.val), 30, 15) FROM #someFloats;

DROP TABLE #someFloats;

-- yields:
--   13.600000000000001

到目前为止,一切都很好-这里不足为奇。我们都知道1.2不能完全用二进制表示形式,因此期望“不精确”的结果。

现在,当我左联接另一个表时,会发生以下奇怪的事情:

CREATE TABLE #A (a int);
INSERT INTO #A (a) VALUES (1), (2);

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT #A.a, STR(SUM(#someFloats.val), 30, 15)
  FROM #someFloats LEFT JOIN #A ON 1 = 1
 GROUP BY #A.a;

DROP TABLE #someFloats;
DROP TABLE #A;

-- yields
--   1   13.600000000000001
--   2   13.599999999999998

sql fiddle,您也可以在那里查看执行计划)

我也有同样的比之和相同的价值观,但不同的浮点错误。如果我向table添加更多行#A,我们可以看到该值在这两个值之间交替。我只能用来重现此问题LEFT JOININNER JOIN在这里按预期工作。

这是不方便的,因为这意味着一个DISTINCTGROUP BYPIVOT将其视为不同的值(这实际上是我们发现了这个问题)。

显而易见的解决方案是舍入该值,但我很好奇:这种行为是否存在逻辑解释?

Answers:


15

实际上,您所指的链接并不表示浮点算术计算始终是确定性的。实际上,在其中一个答案中提到加法不是关联的(含义(a + b) + c不一定相等a + (b + c)),这在该答案中也有提及。

如果流聚合发生时以不同的顺序处理每个组的行,则通常可以自由使用SQL Server;如果ORDER BY在适当的子句中没有,则优化器将选择最快的扫描或查找或其他查询运算符,而与执行添加操作的顺序无关,这可以解释您观察到的行为。

加法始终是确定性的:您放入相同的两个浮点数,就会得到相同的浮点数。但是,以不同的顺序将浮点数相加会得到不同的结果。


关联性与确定性无关,因此有点误导。
Mooing Duck,

浮点加法的非关联性导致SQL Server聚合函数的不确定行为SUM(),您是否同意@MooingDuck?
mustaccio

没有?整数除法就是一个明显的反例。它是非关联的,但完全是确定性的。同样,浮点除法应该是非关联的并且仍然是确定性的。据此,我们得出结论,添加是非关联的并且仍具有确定性是合理的。就是说,如果加法顺序不确定,则结果同样也不确定,因此无论您的第一句话还是最后一句话都是正确的。
Mooing Duck,

整数除法是SQL Server SUM()在浮点参数上的反例,究竟是什么呢?
mustaccio

1
整数除法是非关联性和确定性的。因此,算术运算的关联性与确定性无关。因此,的任何非关联性都SUM()必须与它的确定性无关。我同意这SUM似乎不是确定性的,但是您应该删除提及关联性的内容,因为这无关紧要。
Mooing Duck,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.