-1

In the following script, I multiply many polynomials together and try to get its correct value by setting x=1855.

The answer has to be zero because one of the equation in the multiplication is x-1855 but the answer is way much more than zero after multiply the seventh times.The result shows the polynomial value and the polynomial.

I'm thinking that whether it is overflow somewhere but I am not sure. I need help.Thank you~

from matplotlib import pyplot as plt
import csv
import math
import numpy as np


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')


p1=1
for i in range(1,39):
         p2 = np.poly1d([1,-x[i]])
         p1 = np.polymul(p1,p2)
         print(p2)
         print(p1(1855))

polynomials(first 8)

1 x - 1855, 1 x - 1844 , 1 x - 1828 , 1 x - 1550 ,1 x - 1507 ,1 x - 1496, 1 x - 1486,1 x - 1485 

multiplication

1 x - 1855                                                           -->first
   2
1 x - 3699 x + 3.421e+06                                             -->second
   3        2
1 x - 5527 x + 1.018e+07 x - 6.253e+09                               -->third
   4        3             2
1 x - 7077 x + 1.875e+07 x - 2.204e+10 x + 9.692e+12                 -->forth
   5        4             3             2
1 x - 8584 x + 2.941e+07 x - 5.029e+10 x + 4.29e+13 x - 1.461e+16    -->fifth
   6             5             4             3             2
1 x - 1.008e+04 x + 4.226e+07 x - 9.429e+10 x + 1.181e+14 x - 7.878e+16 x + 2.185e+19 
-->sixth
   7             6             5             4             3
1 x - 1.157e+04 x + 5.723e+07 x - 1.571e+11 x + 2.583e+14 x
              2
 - 2.543e+17 x + 1.389e+20 x - 3.247e+22                             --seventh
   8             7             6             5             4
1 x - 1.305e+04 x + 7.441e+07 x - 2.421e+11 x + 4.915e+14 x
              3             2
 - 6.378e+17 x + 5.166e+20 x - 2.388e+23 x + 4.822e+25               -->eighth
 ....

Result

when x=1855
first one=first two multiplication=...=first six multiplication=0
first seven multiplication=37748736.0
first eight multiplication=558345748480.0

1 Answers1

0

It is not overflow, it is insufficient fp resolution.

Let's have a look at seven factors where you first see the wrong result:

>>> 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])

Let us focus on the last coefficient. We can use np.nextafter to obtain the next representable number in either direction.

>>> first7[-1] - np.nextafter(first7[-1], 0)
-8388608.0    
>>> first7[-1] - np.nextafter(first7[-1], 2 * first7[-1])
8388608.0

As we can see the resolution is much worse than 1, meaning that the coefficients are in practical terms guaranteed to be inaccurate.

If you are only interested in integer coefficients and arguments, you can use Python integers to avoid this problem (at the expense of performance):

>>> Li = L.astype(int).astype(object)
>>> fullprod = ft.reduce(np.polymul, Li)
>>> np.polyval(fullprod, np.array(1855, dtype=object))
0

If you need floating point, then consider using something like bigfloat (sympy might also work).

>>> 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)
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • Thank you Paul. I will try to follow your suggestion~ – Blake Chang Jun 06 '18 at 18:21
  • It worked!! Thank you so much~~ But I am wondering what makes this inaccuracy of precision since all the coefficient are integer, binary number should be able to cover that right? – Blake Chang Jun 15 '18 at 06:51
  • @BlakeChang nope, not fixed-length floating point. FP numbers can express a limited number of decimals (around 15 for float64), so if you store a large number, >10^20, say, you can't cover all places, so you'd have to drop the 5 or so least significant ones. – Paul Panzer Jun 15 '18 at 07:30
  • Got it!! Thank you again Paul! – Blake Chang Jun 22 '18 at 07:40