Почему вызов стандартной суммы в массиве numpy дает результат, отличный от numpy.sum?

Обратите внимание на следующий код: создание массива numpy и вызов встроенной функции Python sum дает результаты, отличные от numpy.sum

Как реализована функция суммы numpy? И почему результат другой?

test = [.1]*10
test = [np.float64(x) for x in test]
test[5]= np.float64(-.9)

d = [np.asarray(test) for x in range(0,60000)]
sum(sum(d))

результаты

np.float64(-1.7473212210461497e-08)

но

np.sum(d)

результаты

np.float64(9.987344284922983e-12)
np.sum(np.sum(d, axis=0)) даст тот же результат, что и sum(sum(d)), с небольшой погрешностью, так что это просто скучная неточность оси/поплавка.
wim 14.08.2024 08:01

@wim Они дают более похожий, но не тот же результат.

Liam385 14.08.2024 08:07

Этот вопрос похож на: Не работает ли математика с плавающей запятой?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.

Julien 14.08.2024 08:12

Судя по первому комментарию, если вы удивлены, что результаты разные, вы, вероятно, также должны быть удивлены, что ни один из них не равен 0?

Julien 14.08.2024 08:14

Это связано с порядком суммирования. Если вы сначала сложите все 0,1, вы получите большой промежуточный результат, что приведет к более высокой неточности конечного результата. Если вы идете по строкам (9x 0,1, 1x -0,9), промежуточный результат остается ближе к 0, сохраняя точность до конца. (редактировать -... опубликовано как ответ..)

Torben Klein 14.08.2024 08:17

Не дурак, имхо; другой вопрос объясняет, почему результат неточен, но не объясняет, почему есть два разных результата (по-видимому) одной и той же операции.

Torben Klein 14.08.2024 08:20

Другая ошибка заключается в том, что сумма Python — это сумма сокращения, а сумма numpy — это попарная сумма. Попробуйте sum([np.float64(1)] + [np.float64(1e-16)]*10).

ken 14.08.2024 08:23

Посмотрите math.fsum, docs.python.org/3/library/math.html

hpaulj 14.08.2024 16:51

@ Liam385 Liam385 Хорошо, похоже. Дело в том, сколько вопросов и ответов нам нужно на этом сайте о неточностях с плавающей запятой, это не очень интересно.

wim 14.08.2024 17:11

Все повторяющиеся вопросы скучны. Все неповторяющиеся вопросы интересны. Людям следует стараться избегать повторения вопросов. Но если есть тема, которая вам скучна, именно вам следует ее избегать.

ken 14.08.2024 19:55

Вопрос не обязательно в неточности ФП, мы все это понимаем. Речь идет о нулевой реализации суммирования. У @ken был ответ, который я искал, кажется, что numpy использует попарное суммирование, с которым я не был знаком

Liam385 15.08.2024 02:16

Мы хотим, чтобы ответы находились в специальном месте «ответ». Наличие ответа внутри вопроса сбивает с толку; мы здесь этого не делаем. Я удалил его; см. ниже, если вам нужны ответы.

anatolyg 16.08.2024 06:35
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
13
98
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это связано с порядком суммирования. Если вы сначала сложите все 0,1, вы получите большой промежуточный результат, что приведет к более высокой неточности конечного результата. Если вы идете по строкам (9x 0,1, 1x -0,9), промежуточный результат остается ближе к 0, сохраняя точность до конца.

Еще варианты:

>>> np.sum(np.sum(arr, axis=0)) # column-wise iteration
-1.747685018926859e-08
>>> np.sum(np.sum(arr, axis=1)) # row-wise iteration, more accurate
-3.3306690738754696e-12
>>> np.sum(arr)
9.987344284922983e-12
>>> sum(sum(arr))
-1.7473212210461497e-08

Еще немного теории:

  • В нотации «e» последняя значащая цифра числа float64 примерно на 16 «меньше» первой.
  • При суммировании чисел точность «потери» можно оценить как количество слагаемых (здесь ~ 6e5).
  • движение по строкам дает промежуточные результаты в диапазоне 1e0. Точность 1e-16. Сумма стоит ~5 цифр: 16-5 = 11 => результат 0 +- 1e-11.
  • двигаясь по столбцам, вы получите промежуточный результат: .1 * 5 * 6e4 = 3e4. Последняя значащая цифра тогда ~ 1e-12. Ошибка, вероятно, возникает в основном из-за вычитания всех .9, которые представляют собой слагаемые 6e4 -> теряется примерно еще 4 цифры => результат равен 0 +- 1e-8. (На самом деле немного больше).

Почему np.sum(np.sum(arr, axis=0)) и sum(sum(arr)) не дают одинаковый результат? Разве они не складывают по одним и тем же осям и в том же порядке? Вопрос снова в том, почему эти вызовы функций, которые логически должны быть одинаковыми, дают разные результаты.

Liam385 15.08.2024 02:11

Хороший вопрос. Возможно, numpy использует дополнительные трюки (см. другой ответ) вместо того, чтобы перебирать числа один за другим.

Torben Klein 15.08.2024 07:57

вот фактическое суммирование numpy, если вам интересно. github.com/numpy/numpy/blob/main/numpy/_core/src/umath/…

Torben Klein 15.08.2024 08:14
Ответ принят как подходящий

Numpy использует попарное суммирование:https://github.com/numpy/numpy/pull/3685 но Python использует суммирование с сокращением.

Ответ лишь частично связан с неточностью FP, потому что, если у меня есть массив чисел FP и я использую один и тот же алгоритм для их суммирования, я должен ожидать того же результата, если суммирую их в одном и том же порядке.

Другие вопросы по теме