Это не переполнение, это недостаточное разрешение fp.
Давайте рассмотрим семь факторов, где вы впервые видите неправильный результат:
>>> import numpy as np
>>> import functools as ft
>>>
>>> x=np.array([1860, 1855, 1844, 1828, 1550, 1507, 1496, 1486, 1485, 1480, 1475, 1442,
... 1032, 950, 593, 543, 499, 243, 211, 200, 189, 173, 168, 152, 141, 140,
... 124, 97, 87, 76, 70, 65, 59, 49, 43, 38,27, 22, 11,],'d')
>>>
>>> L = np.c_[np.ones_like(x), -x]
>>> first7 = ft.reduce(np.polymul, L[:7])
>>> first7
array([ 1.00000000e+00, -1.19400000e+04, 6.10047450e+07, -1.72890531e+11,
2.93522255e+14, -2.98513911e+17, 1.68387944e+20, -4.06415732e+22])
Давайте сосредоточимся на последнемкоэффициент.Мы можем использовать np.nextafter
для получения следующего представимого числа в любом направлении.
>>> first7[-1] - np.nextafter(first7[-1], 0)
-8388608.0
>>> first7[-1] - np.nextafter(first7[-1], 2 * first7[-1])
8388608.0
Как мы видим, разрешение намного хуже, чем 1, что означает, что коэффициенты на практике гарантированно неточны.
Если вас интересуют только целочисленные коэффициенты и аргументы, вы можете использовать целые числа Python, чтобы избежать этой проблемы (за счет производительности):
>>> Li = L.astype(int).astype(object)
>>> fullprod = ft.reduce(np.polymul, Li)
>>> np.polyval(fullprod, np.array(1855, dtype=object))
0
Если вам нужна плавающая точка, тогдарассмотрите возможность использования чего-то вроде bigfloat
(sympy
также может работать).
>>> from bigfloat import BigFloat, setcontext, Context
# set precision (1000 is probably way too much)
>>> setcontext(Context(precision=1000))
# convert array elements
>>> Lb = np.frompyfunc(BigFloat, 1, 1)(L)
# compute product
>>> fullprod = ft.reduce(np.polymul, Lb)
# evaluate
>>> np.polyval(fullprod, BigFloat(1855))
BigFloat.exact('0', precision=1000)