Как объяснено в комментариях, numpy
использует парное суммирование. Таким образом, суммирование (примерно) будет выглядеть так в конце рекурсивного попарного суммирования, когда стек вызовов начнет разрешаться:
(1+1) + (1+1) + (1+1) + (1+1) + (epsilon + epsilon) + ... + (epsilon + epsilon)
(2+2) + (2+2) + (2*epsilon) + (2*epsilon) + ... + (2*epsilon)
(4+4) + (4*epsilon) + (4*epsilon) + ... + (4*epsilon)
8 + (8*epsilon) + (8*epsilon) + ... + (8*epsilon)
8 + (16*epsilon) + (16*epsilon) + ... + (16*epsilon)
...
8 + (999992*epsilon)
Вы правы, утверждая, что 1.0 + epsilon
равно 1.0
. Заманчиво думать, что x + epsilon == x
для всех x
. Это действительно, когда x
является «большим», однако, это не удерживается, когда x == epsilon
(то есть epsilon + epsilon != epsilon
). Таким образом epsilon + epsilon
условия начнут накапливаться:
In [27]: epsilon = 2**(-53)
In [28]: 1.0 + epsilon == 1.0
Out[28]: True
In [29]: 2.0 + epsilon == 2.0
Out[29]: True
In [30]: epsilon + epsilon == epsilon
Out[30]: False
In [31]: epsilon
Out[31]: 1.1102230246251565e-16
In [32]: epsilon + epsilon
Out[32]: 2.220446049250313e-16
In [33]: 123*epsilon
Out[33]: 1.3655743202889425e-14
Я не могу получить numpy
s ответ, но мы можем подойти очень близко:
In [36]: 8 + (999992*epsilon)
Out[36]: 8.000000000111022
In [62]: def pairwise_sum(arr):
...: if len(arr) <= 2:
...: return sum(arr)
...: midpoint = len(arr)//2
...: first_half = arr[:midpoint]
...: second_half = arr[midpoint:]
...: return pairwise_sum(first_half) + pairwise_sum(second_half)
...:
In [63]: pairwise_sum(a)
Out[63]: 8.0000000001110205
Так что здесь явно есть некоторое другое различие (некоторые детали реализации), но, надеюсь, это убеждает вас, что numpy
на самом деле использует парное суммирование.
НТН.