precision in JavaScript numbers

46 views
Skip to first unread message

SpongeJr

unread,
Jun 15, 2015, 11:28:42 PM6/15/15
to khan-academy-javas...@googlegroups.com

Crossposting this from a Khan Academy Question:

(my question):
What would be the most straightforward way to deal with precision errors of this sort?
println(pow(216, 1/3));
> 5.999999999999999

If the answer is to use a library? Which one?
Is there a good solution for this case that would work for the KA JS+PJS environment? (that is, how should I do it without a library?)
I am asking this in the sense of this specific case as well as related cases for math operations where some specific level of precision is required. What are the common solutions to these problems in JavaScript?

There was a response in the comments recently, by Ben Burrill: 

This isn't the best answer, but you can always just round it to 10 decimal places or something. As for arbitrary precision, I think you would either need to use some library or write your own.
What do you need this for?


And my reponse to that:
Well, the specific case was for a game I was writing where I wanted a quick "is the number a perfect cube" test without having to roll my own. I sorted it with something goofy like "do pow(n, 1/3), round to nearest integer, multiply that by itself 3 times and compare with n.".
But I'm also curious what the usual solution is when you need better or abitrary precision, or want to do math with integers, or things like that. What if I had to work with somewhat large numbers, say billions of units, and multiply them by a fraction of a penny each... can I count on JS math to be accurate within a penny? (I don't know, maybe I'm measuring milliliters of consumption of something for some app, and each mL is a fraction of a penny... I don't have any specific use cases, I just want to know what people usually do.)
Many science applications and simulations and such might need better precision, too. I think I've seen some cases here on KA of people having difficulty with total momentum in a system with collisions. I don't know if that's due to JavaScript numbers or not.
I'm aware that JS numbers are 64-bit floating point numbers (IEEE 754), and I realize that's probably what throws computations of this sort off. Even dealing with relatively simple operations on fairly "small" numbers you can introduce small precision errors, and I understand a fair bit about how all of that works, and why, and I've encountered it in other langauges. My main question(s), I think, involve how the issues are usually dealt with in JS.
Thanks for reminding me about this Question! I think I'm going to crosspost it to here and see what sort of info I get from that lot:https://groups.google.com/forum/?hl=en#!forum/khan-academy-javascript-technical-qa


I thought it might be something worth discussing here. It's not anything urgent, or holding me back from a project or anything, but I did want to get some more energy funnelled over here and thought it might be a start.

Larry Serflaten

unread,
Jun 16, 2015, 8:47:36 AM6/16/15
to khan-academy-javas...@googlegroups.com, spon...@gmail.com


 SpongeJr wrote:
 
(my question):
What would be the most straightforward way to deal with precision errors of this sort?
println(pow(216, 1/3));
> 5.999999999999999

If the answer is to use a library? Which one?
Is there a good solution for this case that would work for the KA JS+PJS environment? (that is, how should I do it without a library?)
I am asking this in the sense of this specific case as well as related cases for math operations where some specific level of precision is required. What are the common solutions to these problems in JavaScript?


I thought it might be something worth discussing here. It's not anything urgent, or holding me back from a project or anything, but I did want to get some more energy funnelled over here and thought it might be a start.


I would be on the side of need decides the issue.  If you need a very high degree of precision and accuracy,
then looking for a pre-tested solution (eg. precision library) would be better than trying to go it on your own.

As far as getting the nth root in the general sense, the following routine was tested on KA:


var nthRoot = function(num, nArg, precArg) {
  var n = nArg || 2;
  var prec = precArg || 12;
 
  var x = 1; // Initial guess.
  for (var i=0; i<prec; i++) {
    x = 1/n * ((n-1) * x + (num / Math.pow(x, n-1)));
  }
 
  return x;
};



LFS
 

Larry Serflaten

unread,
Jun 16, 2015, 9:03:30 AM6/16/15
to khan-academy-javas...@googlegroups.com, serf...@gmail.com, spon...@gmail.com


 Larry Serflaten wrote:

As far as getting the nth root in the general sense, the following routine was tested on KA:


var nthRoot = function(num, nArg, precArg) {
  var n = nArg || 2;
  var prec = precArg || 12;
 
  var x = 1; // Initial guess.
  for (var i=0; i<prec; i++) {
    x = 1/n * ((n-1) * x + (num / Math.pow(x, n-1)));
  }
 
  return x;
};



I forgot you were looking for a test of a perfect cube, for that case the test could be something like:

if (nthRoot1(27, 3).toString().indexOf(".")<0){
    println("cube");
}


Which simply converts the root to a string and looks for the decimal point.  Looking at that test now,
I have to wonder if that would work in another locale, one were they use the comma as the decimal point....

LFS


 
 

Bob Lyon

unread,
Jun 16, 2015, 10:11:20 PM6/16/15
to khan-academy-javas...@googlegroups.com
The field is called "Numerical Analysis" and you can take courses on that in college.  I did and what I learned is "don't use floating point - it'll just screw everything up."  And I managed to avoid it during my entire professional career.  However, my dear friend wrote a paper about a quarter century ago whose principles still apply.  Check out "What Every Computer Scientist Should Know About Floating-Point Arithmetic" at http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Your approach for testing if an integer is a perfect square of another integer by cubing the rounded cube root of a number will certainly work for numbers less than 2 to the 54th power. After that, integer values cannot be precisely represented, so the issue becomes meaningless.

The original question asked about dealing with 5.999999999999999 when 6 is the correct number. The easiest answer is to ignore it. That said, the most dangerous operation that you'll do with floating numbers is to check them for (in)equality. One often introduces an application specific epsilon which is nearly zero and tests if |a - b| < epsilon. If so, the application concludes that a and b are equal.

SpongeJr

unread,
Jun 18, 2015, 12:03:09 AM6/18/15
to khan-academy-javas...@googlegroups.com
Thanks for all of the great information, both of you! I'm more confident now in how I'm dealing with it, and more informed on many levels about several things.

Reply all
Reply to author
Forward
0 new messages