Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to deal with "has more than 15 significant digits" in v6? #148

Closed
morioust opened this issue Mar 10, 2018 · 16 comments
Closed

How to deal with "has more than 15 significant digits" in v6? #148

morioust opened this issue Mar 10, 2018 · 16 comments

Comments

@morioust
Copy link

I looked at the documentation and the issues regarding this in the past, but I couldn't figure out a definite answer.

The solution from #123 doesn't seem to work anymore.
From #139 it almost seems to me that there's no way to construct a BigNumber with more than 15 significant digits. Is that really the case?

A common use case for example is to handle numbers from a d3 scale and handle them after:

const x = d3.scaleLinear().domain([10, 100]).range([1, 1.003]);
const pos = x(99); // 1.0029666666666666
new BigNumber(pos); // Number primitive has more than 15 significant digits: 1.0029666666666666

Is it really necessary to manipulate the number before passing it to BigNumber?

@MikeMcl
Copy link
Owner

MikeMcl commented Mar 10, 2018

The ERRORS option has been removed in the lastest version.

To create a BigNumber from a number with more than 15 significant digits it's as simple as

new BigNumber(pos.toString());

@morioust
Copy link
Author

Ok, that works, thanks!

@fresheneesz
Copy link

This is pretty annoying to me. I don't want to have to care where the error is, I just want it to be able to handle it and I'll accept any lack of precision. Or if tostringing it is all that's needed and it can do full precision, then why doesn't it just do that internally without bothering the library user?

@tab00
Copy link

tab00 commented Apr 5, 2018

This problem broke my app in so many places. It should have shown a warning, not cause a catastrophic error.

@jtakalai
Copy link

What is that error message trying to say anyway? What's special about 15 significant digits? Why should I care? How on Earth is "convert it toString first" a solution? Is this the state-of-the-art of numbers in JS?

@MikeMcl
Copy link
Owner

MikeMcl commented Apr 26, 2018

Published v7.0.0 which no longer throws an error on numbers with more than 15 significant digits unless a DEBUG property is added to BigNumber and set to true

// No error, and precision is lost
let x = new BigNumber(95687346387453987.453226);     // '95687346387453980'

BigNumber.DEBUG = true;

let y = new BigNumber(95687346387453987.453226);    // '[BigNumber Error]
// 'Number primitive has more than 15 significant digits'

@jdalrymple
Copy link

Could the number not be converted toString internaly when its larger than 15 sf? Or am i missing something

@MikeMcl
Copy link
Owner

MikeMcl commented Apr 26, 2018

@jdalrymple

I am not sure what you are asking or why you have phrased your question like there is a problem seeking a solution.

console.log(95687346387453987.453226)      // '95687346387453980'

Of course, if a user wants to avoid the precision loss shown in the above post, they can just pass the value as a string

let x = new BigNumber('95687346387453987.453226');    // '95687346387453987.453226'

@jdalrymple
Copy link

Oh sorry, I thought the precision loss wasn't a desired thing.

@fresheneesz
Copy link

fresheneesz commented Apr 26, 2018

@MikeMcl Thanks, I think this is an improvement. But I'm curious as to why you made the design decision to choose between throwing an error and losing precision. What prevents us from stringifying the argument passed into BigNumber() in the case it has too many digits? Ie, I'm wondering why we can't both keep full precision and still not throw an error in this case.

@MikeMcl
Copy link
Owner

MikeMcl commented Apr 26, 2018

@fresheneesz

You can of course keep full precision if you pass the argument as a string. This issue is about what happens with number primitives only.

What prevents us from stringifying the argument passed into BigNumber() in the case it has too many digits?

Nothing. That is the recommended thing to do - pass values as strings to avoid possible precision loss.

I think the last few comments show there is some misunderstanding here, but I'm not sure what I need to try and clarify.

Look again at the example I gave above:

console.log(95687346387453987.453226)      // '95687346387453980'

Within the implementation of the console.log function there is no access to the value 95687346387453987.453226, it is stored as a JavaScript number with the value 95687346387453980 as soon as it is parsed by the JavaScript engine - the precision is lost before the function sees it.

@jdalrymple
Copy link

I guess what I was trying to ask was why allow for the loss of precision at all?

Whether the user passes in a number or a string, internally convert to a string to not lose the precision.

@MikeMcl
Copy link
Owner

MikeMcl commented Apr 26, 2018

@jdalrymple

See my addition to my last comment, and ask further questions based on that example please if necessary.

There is more (too much) background on this issue here also.

@tab00
Copy link

tab00 commented Apr 27, 2018

That is the recommended thing to do - pass values as strings to avoid possible precision loss.

It would have been better if you had actually stated that in the official documentation page at http://mikemcl.github.io/bignumber.js.

@fresheneesz
Copy link

fresheneesz commented Apr 27, 2018

@MikeMcl

I think the last few comments show there is some misunderstanding here

I think there was some misunderstanding. But I think looking at your code makes things a bit clearer. You use str.replace(/^0\.0*|\./, '').length > 15 to detect whether there are more than 15 significant digits. However, I don't believe that code is correct for telling you the number of significant digits. What it seems to tell you is how many digits there are, whether or not they're significant.

Improving that detection might be in order. For example, while the following returns true:

(99999111100000000000+'').replace(/^0\.0*|\./, '').length > 15

this returns false:

(99999111100000000000000000+'').replace(/^0\.0*|\./, '').length > 15

even tho the number of actual significant digits are the same.

Also, this error is confusing because its not at all clear why the code cares about this. What I would suggest is to change this error to: "Passed in number has more than 15 digits, indicating that the primitive has likely been subject to a loss of precision. If this number is an immediate, pass it in as a string in order to preserve precision.". Then it would be immediately obvious both why the code is bothering you and what you can do about it.

@MikeMcl
Copy link
Owner

MikeMcl commented Apr 27, 2018

@tab00

Okay, I may add a note to the README.

@fresheneesz

If you type 99999111100000000000000000 into the console and press Enter you will see that it returns 9.99991111e+25 which is a number with 9 significant digits, while 99999111100000000000 returns the same as its self, a number with 20 significant digits.

The check counts integer-part trailing zeros as significant, but the 15 s.d. limit applies to the value of the number (as shown by valueOf and toString), not the value of the number literal in the source code.

There is no way of detecting that the 9.99991111e+25 originated from a number literal with the value 99999111100000000000000000.

x = 99999111100000000000000000;
x.valueOf();     //  9.99991111e+25
x.toString();    // '9.99991111e+25'

x = 99999111100000000000000001;
x.valueOf();     //  9.99991111e+25
x.toString();    // '9.99991111e+25'

The limitations of the 15 significant digit check are discussed at the link I gave above, here.

I will add further explanantion of the limit to the documentation.

Repository owner locked as resolved and limited conversation to collaborators Apr 27, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants