josdejong / mathjs

An extensive math library for JavaScript and Node.js
https://mathjs.org
Apache License 2.0
14.44k stars 1.24k forks source link

Convert decimal to number without losing precision #753

Closed rizwanspeaks closed 6 years ago

rizwanspeaks commented 8 years ago

I am getting the result with losing precision when converting to the number). Note: [I am configured number as BigNumber and precision as 20]

` math.config({ number: 'bignumber', precision: 20 });

   var inputVal =  "182181992660526004";
   var inputValAsBigNum = math.bignumber(inputVal); 
   var resultAsDecimal = math.divide(inputValAsBigNum, math.bignumber(90000));
   var resultAsStr = resultAsDecimal .toString();//"2024244362894.7333778"
   var resultAsNumber = math.number(resultAsDecimal );//2024244362894.7334`

Here the expected result for resultAsNumber is 2024244362894.7333778 instead I am getting 2024244362894.7334 as result.

josdejong commented 8 years ago

If you do calculations with BigNumbers having 20 digits, you can up with a result having a precision less than 20 digits as rounding errors can occur.

For example, suppose you work with 2 digits precision and you calculate 1/3 + 1/3, you will get 0.33 + 0.33 = 0.66 whilst the correct answer would be 0.67.

The solution is to always work with a higher precision than you need in the end. If you want 20 digits precision, work with 24 or 32 digits so you don't end up with round off errors in the first 20 digits.

rizwanspeaks commented 8 years ago

In the below code

var resultAsNumber = math.number("2024244362894.73337782345");//2024244362894.7334`

math.number() method truncate the input by rounding the decimal by 4 digits. Is it possible to increase the rounding value.(I need 2024244362894.73337782345 as result instead of 2024244362894.7334

Note: Here I set the 'precision' value as 24

josdejong commented 8 years ago

Regular numbers in JavaScript (and most programming languages) have a precision of about 16 digits. With math.number() you convert the value to a regular number, which can't hold the 20 digits you want. You have to use BigNumbers for that. So, keep using resultAsDecimal, don't create a resultAsNumber.

If you want to get stringified output with a certain precision, you can use math.format, like:

var precision = 20;
var resultAsString = math.format(inputValAsBigNum, precision);

See relevant docs: http://mathjs.org/docs/reference/functions/format.html http://mathjs.org/docs/datatypes/numbers.html http://mathjs.org/docs/datatypes/bignumbers.html

munrocket commented 6 years ago

@josdejong what you think about double.js library for extended precision? Can it be usefull for math.js somehow?

josdejong commented 6 years ago

@munrocket thanks for your suggestion. Do you see any advantages of double.js over decimal.js? (The latter is already available in mathjs)

munrocket commented 6 years ago

Yep, it's much faster than decimal.js, but it handle only approximately 32 digits. It can be useful for heavy computation with double-double precision or as intermidiate format for numerical differentiation / matrix elimination.

josdejong commented 6 years ago

That is definitely a valid reason. I'm not sure how common the need for double precision is though, in my experience 16 digits is just fine normally and the speed is important, and if numerical solutions get unstable due to round off errors they go unstable with some more digits too. But that's just my experience. I'm certainly open to add a new numeric type double.js if there is a broad need for it.

An other numeric solution that sound really interesting to me is http://dec64.com/, which aims not just to have "more digits", but really addresses the round off issues that we typically have for example when working with money (you know the famous 0.1+0.2 I suppose...).

munrocket commented 6 years ago

Very intresting link! Base 2 is too old and goes from Niklaus Virt. We need dec64 in hardware. Yep double.js can handle 0.1+0.2. But not all of them for now.