Недетерминированная сумма чисел с плавающей точкой


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 , вы также можете увидеть план выполнения там)

У меня та же сумма по тем же значениям, но другая ошибка с плавающей точкой. Если я добавлю больше строк в таблицу #A, мы увидим, что значение чередуется между этими двумя значениями. Я смог воспроизвести эту проблему только с помощью LEFT JOIN; INNER JOINработает как положено здесь.

Это неудобно, потому что это означает, что DISTINCT, GROUP BYили PIVOTвидит их как разные значения (что на самом деле, как мы обнаружили эту проблему).

Очевидное решение заключается в округлении значения, но мне любопытно: есть ли логическое объяснение этому поведению?

Ответы:


15

На самом деле ссылка, на которую вы ссылаетесь, не говорит о том, что арифметические вычисления с плавающей запятой всегда детерминированы. Фактически, в одном из ответов упоминается, что сложение не ассоциативно (значение (a + b) + cне обязательно равно a + (b + c)), что также сказано в этом ответе .

Если происходит агрегация потоков для обработки строк каждой группы в разном порядке - что обычно делает SQL Server бесплатно; если ORDER BYв соответствующем предложении ничего нет , то оптимизатор выберет любой тип сканирования или поиска, или другой оператор запроса будет самым быстрым, независимо от того, в каком порядке выполняются добавления, - это может объяснить поведение, которое вы наблюдаете.

Добавление всегда детерминировано: вы кладете в те же два поплавка, вы получаете тот же самый поплавок. Но добавление поплавков в другом порядке может дать другой результат.


Ассоциативность не имеет отношения к детерминизму, так что бит вводит в заблуждение.
Mooing Duck

SUM()Неассоциативность сложения с плавающей точкой приводит к недетерминированному поведению агрегатной функции SQL Server , согласитесь ли вы @MooingDuck?
mustaccio

Нет? Целочисленное деление является явным контрпримером. Это неассоциативно, но полностью детерминировано. Аналогично, деление с плавающей точкой должно быть неассоциативным и все же детерминированным. Исходя из этого, мы заключаем, что разумно, чтобы дополнение было неассоциативным и все же детерминированным. При этом, если порядок дополнений не является детерминированным, то результат также не будет детерминированным, поэтому ваше первое и последнее предложение по-прежнему правильны, независимо от этого.
Mooing Duck

Целочисленное деление является контрпримером для SQL Server SUM()по аргументам с плавающей запятой, как именно?
mustaccio

1
Целочисленное деление неассоциативно и детерминировано. Поэтому арифметические операции ассоциативности не связаны с детерминизмом. Поэтому любая неассоциативность SUM()должна быть неуместна в отношении ее детерминизма. Я согласен, что это SUMкажется недетерминированным, но вы должны удалить упоминания об ассоциативности, поскольку это не связано.
Mooing Duck
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.