tomas.teicher
tomas.teicher

Reputation: 923

Javascript Math.cos and Math.sin are inaccurate. Is there any solution?

JavaScript Math trigonometrical methods return wrong results.

alert(Math.sin(Math.PI));
// sin(180) in degrees should return 0, but above code doesn't

it doesn't return 0.

Maybe problem is with JavaScript decimal number precision. Is there any workaround to get correct results?

Upvotes: 22

Views: 10373

Answers (3)

Alisa Sireneva
Alisa Sireneva

Reputation: 161

This is actually not a bug, and not even a rounding error from Math.sin itself.

Mathematics says sin(π) = 0. but Math.PI is only an approximation of π, so, mathematically, sin(Math.PI) is not zero. The value is also pretty clearly not a denormal, so it's not unexpected to get a non-zero value.

In fact, as the derivative of sin near π is around -1, we can estimate that sin(x) ≈ π - x (this is effectively an iteration of Newton's method). Substituting Math.PI, we obtain π ≈ Math.PI + sin(Math.PI) ≈ Math.PI + Math.sin(Math.PI).

Indeed:

> Math.PI.toString(16)
'3.243f6a8885a3'
> Math.sin(Math.PI).toString(16)
'0.00000000000008d313198a2e038'

(I'm using hexadecimal because floats aren't precisely formatted when using decimal)

The sum of these two numbers when computed by hand is 3.243f6a8885a308d313198a2e038, or, converted to decimal, 3.14159265358979323846264338327950587... This approximation of π is valid for 33 decimal digits, as opposed to the 17-digit approximation provided by Math.PI -- almost doubling the precision. Of course, you can't actually work with such a number in JS's builtin floats, but there are tricks such as double-double arithmetic.

Anyway: Math.sin is, in fact, extremely accurate -- it's Math.PI that introduces the error!

Upvotes: 1

Davenchy
Davenchy

Reputation: 57

you can use

Math.sin(Math.PI).toFixed(3) // "0.000"

Examples:

const cos = (a) => Math.cos(Math.PI * a / 180); cos(90) // 6.123233995736766e-17

then you can use .toFixed()

cos(90).toFixed(3) // "0.000"

Note

.toFixed() returns string, so you can parse it to float using parseFloat()

parseFloat(cos(90).toFixed(3)) // 0.000

Upvotes: 4

Andrew Cooper
Andrew Cooper

Reputation: 32586

It's very, very close to zero, though. (~ 10^-16)

And alert(Math.sin(Math.PI/2)) does return 1.

It's just one of things you have to be careful of when dealing with floating point arithmetic. Rounding errors pop up all over the place.

Upvotes: 16

Related Questions