0

I encountered an odd situation in Python (version 3.9.8) and numpy (version 1.20.0)trying to convert float64 numbers to int. It started with calculating the fft coefficients with numpy and then using inverse DFT with

x = np.array([1, -4, 2, 2, 1, -4, 6, 2]) # test vector
X = np.fft.fft(x)                        # calc fft coefficients

The result is fine so far:

print(X)
[ 6. +0.j  0. +4.j -6.+12.j  0. -4.j 14. +0.j  0. +4.j -6.-12.j  0. -4.j]

Now I've written my own inverse DFT and get correct results:

N = len(X)       # number of samples N
y = np.zeros(N)  # create empty result vector with N elements

# calc inverse DFT
for n in np.arange(N):
    
    sum = 0
    
    for k in np.arange(N):
        sum = sum + X[k] * np.exp(1j * 2* np.pi * k * n/N)
        
    y[n] = sum.real/N

# print results
print(y)
[ 1. -4.  2.  2.  1. -4.  6.  2.]

The resulting vector is also fine.

But when I convert this vector to int strange things happen. IIn the second half of the vector, values change in ways that are inexplicable to me.

print(y.astype('int'))

[ 1 -4  2  2  0 -3  6  1]

Any idea?

Gerold
  • 3
  • 1
  • `y - x` will reveal the inaccuries that have built up, which then result to the rounding artifacts you see when you convert them to integers. – Reti43 Nov 25 '21 at 11:11
  • @Reti43 It explains the underlying cause, but not how to solve OPs problem. – 9769953 Nov 25 '21 at 11:12
  • @9769953, do we need to solve the OPs problem? The question states "_values change in ways that are inexplicable to me_" and I think the duplicate makes it explicable. And knowing the cause makes it possible to solve the problem, without having to create an answer for every possible program where rounding errors occur. – wovano Nov 25 '21 at 11:25
  • @wovano Well, it's nice and practical to show a solution to the actual problem as well, not just explain the underlying problem. Knowing the problem, doesn't mean one actually knows how to solve it (e.g., where to look for relevant functionality). Both an explanation and solution would be even better, but the duplicate doesn't do that as far as a I can tell: it just had hundreds of lines spread across top answers on how floating point math works. – 9769953 Nov 25 '21 at 11:31
  • @9769953, but if you know it's a rounding issue (or actually the lack of rounding), then you could search for [`[python] [numpy] astype round`](https://stackoverflow.com/search?q=%5Bpython%5D+%5Bnumpy%5D+astype+round) and you would find [this question](https://stackoverflow.com/questions/43910477/numpy-astype-rounding-to-wrong-value) with a perfectly useful answer (admittedly, this might be a more accurate duplicate target then the one proposed earlier). Unfortunately, that answer has now been duplicated as well... – wovano Nov 25 '21 at 14:24
  • That question is absolutely a better duplicate. The answer briefly explains the underlying issue, and shows a solution. Basically the same as my answer here. – 9769953 Nov 25 '21 at 14:27

2 Answers2

0

astype(int) will truncate values to integers: it just ditches the part after the decimal period. And the floating point values you see on your screen, are rounded representations: 2. is probably something like 1.99999999 because of floating point imprecisions.

First, round the values, then convert them to integer:

np.round(y)

produces

array([ 1., -4.,  2.,  2.,  1., -4.,  6.,  2.])

which are still floating point values, but now rounded to integer values (note that all (machine-range) integers can be represented exactly by floating point numbers).

Thus do the following:

np.round(y).astype(int)

to get

array([ 1, -4,  2,  2,  1, -4,  6,  2])
9769953
  • 10,344
  • 3
  • 26
  • 37
0

Here is a quick explaination of the issue with the solution:

import numpy

lst = numpy.array([-3.99999999999])

print(f"{lst = }")  # array([-4.])
print(f"{lst.astype('int')}")  # array([-3])
print(f"{numpy.round(lst).astype('int')}")  # array([-4])

It is just a display error here. the print of the numpy array don t show you all digits.

Tranform in an integer will remove all digits of float. Then if you are under 4 (even at e-10) you will be int at 3. You have to round your result before change to int type.

print(int(3.99999999))  # 3
print(round(3.99999999))  # 4
Vincent Bénet
  • 1,212
  • 6
  • 20
  • In the prints displayed in comments – Vincent Bénet Nov 25 '21 at 11:31
  • 1
    It doesn't explain why `lst.astype('int')` goes from -3.99999999999 to -3, instead of -4. It shows it does, but doesn't give the underlying reason. – 9769953 Nov 25 '21 at 11:33
  • Well it is obvious for me that is a display error. Printing a numpy array wil round at 6 digits. Tranform in an integer will remove all digits of float. Then if you are under 4 (even at e-10) you will be int at 3. – Vincent Bénet Nov 25 '21 at 11:36