I am trying to generate a square wave starting from a sine wave using numpy.
I ran into a problem where:
- using the
numpy.piconstant gives inaccurate results
- using the
3.14constant gives me the correct result
To create the square wave the sign of the sine wave is taken and then the -1s are transformed into 0s using the maximum function.
N = 15 x = np.arange(1, N) s = np.maximum(np.sign(np.sin(3.14 * (x - 1))), np.zeros_like(x)) print(s) >> [0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1.]
While using the constant for pi in numpy gives:
N = 15 x = np.arange(1, N) s = np.maximum(np.sign(np.sin(np.pi * (x - 1))), np.zeros_like(x)) print(s) >> [0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 0.]
where the sequence is not alternating 1s and 0s. With more elements you can observe patches of more than two 0s next to each other.
[0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 0. 0. 1. 0. 0. 0.]
I am mostly trying to understand how using more precise values can lead to imprecise results.
Your computation is numerically unstable. Using a higher precision will not solve the problem but just hide it a little bit more. The main issue is that
sin(PI * x) should be theoretically always 0, but no floating-point function is perfect, so there is an error (typically 1 ULP). This error change the sign of the result. This is what you observe. This problem is independent of the precision. Here is a geometrical illustration of what you compute:
If you just want alternated 0-1 values, you can add a shift to the
sin function (eg.
np.maximum(np.sign(np.sin(np.pi * (x - 1) + np.pi/2)), np.zeros_like(x))). If the shift is
pi/2, then you can use a