浮点数的非确定性总和

Hei*_*nzi 11 sql-server floating-point

让我说出显而易见的拳头:我完全理解浮点类型不能准确表示十进制值。这不是关于那个!尽管如此,浮点计算应该是确定性的

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

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
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都很好 - 这里没有惊喜。我们都知道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
Run Code Online (Sandbox Code Playgroud)

sql fiddle,你也可以在那里看到执行计划)

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

这很不方便,因为这意味着 a DISTINCT, GROUP BYorPIVOT将它们视为不同的值(这实际上是我们发现此问题的方式)。

显而易见的解决方案是四舍五入,但我很好奇:这种行为是否有合乎逻辑的解释?

mus*_*cio 15

实际上,您所指的链接并没有说浮点算术计算始终是确定性的。事实上,在其中一个答案中提到加法不是关联的(意思(a + b) + c不一定等于a + (b + c)),这也在这个答案中说。

如果流聚合碰巧以不同的顺序处理每个组的行——SQL Server 通常可以随意执行;如果ORDER BY在适当的子句中没有,那么优化器将选择任何扫描或查找或其他查询运算符将是最快的,无论执行添加的顺序如何——那么这可以解释您观察到的行为。

加法总是确定性的:你放入相同的两个浮点数,你得到相同的浮点数。但是以不同的顺序将浮点数添加在一起会产生不同的结果。