Closed abhikhatri closed 6 years ago
This is because of round off errors, see #447.
Thanks 😄
You're welcome.
I followed the referenced thread but it's throwing an error:
Unexpected type of argument in function cos (expected: number or Array or Matrix, actual: BigNumber, index: 0)
when I add math.config({number: 'BigNumber'});
.
I cannot reproduce that:
math.config({number: 'BigNumber'});
var bignumber = math.parse('cos(90 deg)').eval();
bignumber.toString(); //"1.539082031431044993140174126710585339910740432566411533235469223e-64"
Although it is still not 0. But keep in mind that math.js is not an algebraic but a numeric library.
@abhikhatri are you using the latest version of mathjs?
@josdejong I updated it today after I saw this bug. @FSMaxB I am using this example to switch modes and apparently this was causing that exception.
when I commented out math.import(replacements, {override: true});
it was working fine.
Is there any workaround to get the correct values of sin, cos and tan?
@abhikhatri the example you link to does indeed only work for numbers, not for BigNumbers. We could probably extend the example. You can also work it out yourself for BigNumbers if needed.
Is there any workaround to get the correct values of sin, cos and tan?
This is a fundamental issue of numerical applications working with floating point numbers. The only real solution is using an algebra system instead (which does symbolic computation). The docs on numbers and BigNumbers give you some more explanation and some tips on how to deal with this:
http://mathjs.org/docs/datatypes/numbers.html#roundoff-errors http://mathjs.org/docs/datatypes/bignumbers.html#limitations
The angle configuration example saved me!!!! 😃 Instead of bashing with the core library I messed with that example and finally got it working.
var trigPrecision = 1000000000;
switch (config.angles) {
case 'deg':
var angleDivisor = 180 / Math.PI;
var eq = fn(x / angleDivisor);
return Math.round(eq * trigPrecision) / trigPrecision;
case 'grad':
return fn(x / 400 * 2 * Math.PI);
default:
return fn(x);
}
I've changed convert to degree method and added this simple code snippet to fix rounding error. It's working fine for me now 💃
To change the precision simply change the number of 0s in trigPrecision
When the cosine of a unit is calculated could there not be a check to see if it was exactly 90deg
, 100grad
, etc and if so return zero?
@abhikhatri good to hear. For the rounding you could also use math.round(eq, numberOfDecimals)
.
@HarrySarson that's indeed a solution: create a set of special cases for which you return an exact answer. Another solution is to reduce the input to the range of [-pi, pi), since trigonometric functions get more inaccurate the further away you are from zero.
@josdejong Thanks 😄
Just for laughs :smiley:
I came across this issue before. Determined to find a better solution than rounding I came up with this ridiculous contrived method for sin with positive values. You should be able to adapt it for the other trig functions if you are crazy enough, might need branching for negatives:
The idea is to exploit all of the symmetry in the sine function, one period of a sine wave has 4 identical sections which are mirrored in some way. The sin function can correctly calculate the intersection at zero and PI / 2 but not higher so the first quarter is good.
The first part of the expression folds all of the quarters into the first one... this is fine for the first half period which have the same output reflected, but the second half looses it's sign. So the second part of the expression multiplies by 1 or -1 depending on x.
f(x) = sin((PI / 2) - abs((x % PI) - (PI / 2))) * (1 - 2 * floor((x / PI) % 2))
f(PI * 0) // 0
f(PI * 0.5) // 1
f(PI * 1) // 0
f(PI * 1.5) // -1
f(PI * 2) // 0
You might notice that if you use radians then it's still reliant on the input being calculated with the same precision of PI that is being used to fold the function up. Instead if you make the boundaries into rational numbers by using degrees then it works perfectly (replacing PI with 180):
f(x) = sin(unit(90 - abs((x % 180) - 90), "deg") * (1 - 2 * floor((x / 180) % 2))
f(0) // 0
f(90) // 1
f(180) // 0
f(270) // -1
f(360) // 0
@ThomasBrierley yes I meant something like that with reducing the input range though I wasn't aware of this formula :)
Hey there,
I recently started working with mathjs and I seriously love this library. It has everything anyone can ask for but recently I came across an issue that is bothering me. Whenever I run
math.eval('cos(90 deg)')
it gives me6.123233995736766e-17
instead of0
and same issue withtan
. Whenever I runmath.eval('tan(90 deg)')
it gives me16331239353195370
instead of an error.Is there way to fix that cause I am not sure If I am doing it wrong or it's actually throwing incorrect answers?