MCF
MCF

Reputation: 393

Why is sin(180) not zero when using python and numpy?

Does anyone know why the below doesn't equal 0?

import numpy as np
np.sin(np.radians(180))

or:

np.sin(np.pi)

When I enter it into python it gives me 1.22e-16.

Upvotes: 28

Views: 29143

Answers (6)

tonya michael
tonya michael

Reputation: 1

Python uses the normal taylor expansion theory it solve its trig functions and since this expansion theory has infinite terms, its results doesn't reach exact but it only approximates. For e.g sin(x) = x - x³/3! + x⁵/5! - ...

=> Sin(180) = 180 - ... Never 0 bout approaches 0.

That is my own reason by prove.

Upvotes: 0

Beomin
Beomin

Reputation: 1

Simple.

np.sin(np.pi).astype(int)
np.sin(np.pi/2).astype(int)
np.sin(3 * np.pi / 2).astype(int)
np.sin(2 * np.pi).astype(int)

returns

0
1
0
-1

Upvotes: -3

Meric Ozcan
Meric Ozcan

Reputation: 818

Faced same problem,

import numpy as np

print(np.cos(math.radians(90)))

>> 6.123233995736766e-17

and tried this,

print(np.around(np.cos(math.radians(90)), decimals=5))

>> 0

Worked in my case. I set decimal 5 not lose too many information. As you can think of round function get rid of after 5 digit values.

Upvotes: 2

pico
pico

Reputation: 1910

Try this... it zeros anything below a given tiny-ness value...

import numpy as np

def zero_tiny(x, threshold):
    if (x.dtype == complex):
        x_real = x.real
        x_imag = x.imag
        if (np.abs(x_real) < threshold): x_real = 0
        if (np.abs(x_imag) < threshold): x_imag = 0 
        return x_real + 1j*x_imag
    else:
        return  x if (np.abs(x) > threshold) else 0

value = np.cos(np.pi/2)
print(value)
value = zero_tiny(value, 10e-10)
print(value)

value = np.exp(-1j*np.pi/2)
print(value)
value = zero_tiny(value, 10e-10)
print(value)

Upvotes: 0

pico
pico

Reputation: 1910

One solution is to switch to sympy when calculating sin's and cos's, then to switch back to numpy using sp.N(...) function:

>>> # Numpy not exactly zero
>>> import numpy as np
>>> value = np.cos(np.pi/2)
6.123233995736766e-17

# Sympy workaround
>>> import sympy as sp
>>> def scos(x): return sp.N(sp.cos(x))
>>> def ssin(x): return sp.N(sp.sin(x))

>>> value = scos(sp.pi/2)
0

just remember to use sp.pi instead of sp.np when using scos and ssin functions.

Upvotes: 3

abarnert
abarnert

Reputation: 365697

The number π cannot be represented exactly as a floating-point number. So, np.radians(180) doesn't give you π, it gives you 3.1415926535897931.

And sin(3.1415926535897931) is in fact something like 1.22e-16.

So, how do you deal with this?

You have to work out, or at least guess at, appropriate absolute and/or relative error bounds, and then instead of x == y, you write:

abs(y - x) < abs_bounds and abs(y-x) < rel_bounds * y

(This also means that you have to organize your computation so that the relative error is larger relative to y than to x. In your case, because y is the constant 0, that's trivial—just do it backward.)

Numpy provides a function that does this for you across a whole array, allclose:

np.allclose(x, y, rel_bounds, abs_bounds)

(This actually checks abs(y - x) < abs_ bounds + rel_bounds * y), but that's almost always sufficient, and you can easily reorganize your code when it's not.)

In your case:

np.allclose(0, np.sin(np.radians(180)), rel_bounds, abs_bounds)

So, how do you know what the right bounds are? There's no way to teach you enough error analysis in an SO answer. Propagation of uncertainty at Wikipedia gives a high-level overview. If you really have no clue, you can use the defaults, which are 1e-5 relative and 1e-8 absolute.

Upvotes: 29

Related Questions