Neo4j с плавающей точкой сумма разных результатов - PullRequest
1 голос
/ 14 июня 2019

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

...
WITH foo
ORDER BY foo.fooId
RETURN SUM(foo.Weight)

Возвращает результат, отличный от запроса, который просто суммирует:

...
RETURN SUM(foo.Weight)

Различия незначительны (293.07724195098984 против 293.07724195099007). Но этого достаточно, чтобы простые проверки на равенство провалились. Другим примером может быть другой экземпляр базы данных, загруженный с одинаковыми данными с использованием одного и того же процесса загрузки, может вызвать ту же проблему (базы данных могут быть не 1: 1, порядок загрузки некоторых отношений может отличаться). Я взял необработанные значения сумм neo4j (просто удалив SUM()) и убедился, что они одинаковы во всех случаях (разные БД и упорядоченные / не упорядоченные).

Какие у меня есть варианты? Я не возражаю потерять некоторую точность (я уже пытался снизить точность с 15 до 12 десятичных знаков, но это не сработало), но мне нужны результаты, чтобы соответствовать.

1 Ответ

2 голосов
/ 14 июня 2019

Из-за ошибок округления числа с плавающей точкой не являются ассоциативными.(a + b) + c! = a + (b + c).
Результат каждой операции округляется, чтобы соответствовать ограничениям кодирования чисел с плавающей запятой, и (a + b) + c реализуется как round (round (a + b)) + c) пока a + (b + c) как раунд (a + round (b + c)).

В качестве очевидной иллюстрации рассмотрим операцию (2 ^ -100 + 1 -1).Если интерпретировать как (2 ^ -100 + 1) -1, он вернет 0, так как 1 + 2 ^ -100 потребует слишком большой точности для чисел с плавающей запятой или двойного кодирования в IEEE754 и может быть закодирован только как 1.0.Хотя (2 ^ -100 + (1-1)) правильно возвращает 2 ^ -100, который может быть закодирован как с плавающей запятой, так и с удвоением.
Это тривиальный пример, но эти ошибки округления могут существовать после каждой операции и объяснять, почемуОперации с плавающей запятой не ассоциативны.

Базы данных, как правило, не возвращают данные в гарантированном порядке, и в зависимости от фактического заказа операции будут выполняться по-разному, что объясняет ваше поведение.

В целом по этой причинеЭто не очень хорошая идея, чтобы сделать сравнение равенства на поплавках.Обычно рекомендуется заменить a == b на abs (ab) "достаточно" мало.
"достаточно" может зависеть от вашего алгоритма.float эквивалентен ~ 6-7 десятичным знакам и удваивается до 15-16 десятичных знаков (и я думаю, что это то, что используется в вашей БД).В зависимости от количества вычислений могут быть затронуты последние 1–3 десятичных знака.
Лучше всего использовать
abs (ab)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...