Недетермінована сума плавців


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 , ви також можете побачити план виконання)

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

Це незручно, тому що це означає , що DISTINCT, GROUP BYабо PIVOTбачить їх як різні значення (які насправді , як ми виявили , це питання).

Очевидним рішенням є округлення значення, але мені цікаво: чи є логічне пояснення такої поведінки?

Відповіді:


15

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

Якщо агрегація потоків трапляється для обробки рядків кожної групи в різному порядку - що зазвичай дозволяє SQL Server; якщо ORDER BYу відповідному пункті немає, оптимізатор вибере будь-яке сканування чи пошук, або інший оператор запитів буде найшвидшим, незалежно від того, який порядок виконує додавання - тоді це може пояснити поведінку, яку ви спостерігаєте.

Додавання завжди детерміноване: якщо ви введете в один і той же два поплавці, ви отримаєте той самий поплавок. Але додавання плавців разом в іншому порядку може дати інший результат.


Асоціативність не має відношення до детермінізму, тому цей біт вводить в оману.
Mooing Duck

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

Ні? Відділ Integer - це чіткий контрприклад. Він не асоціативний, але цілком детермінований. Так само поділ з плаваючою комою має бути неасоціативним та все ж детермінованим. Виходячи з цього, ми робимо висновок, що доцільно, щоб додавання було не асоціативним та все ж детермінованим. Якщо говорити, якщо порядок доповнень не є детермінованим, то результат також не буде детермінованим, тому ваше перше та останнє речення все ще правильне, незалежно.
Mooing Duck

Поділ цілого числа є контрприкладом для SQL Server SUM()над аргументами з плаваючою комою, як саме?
мустаччо

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