This question already has answers here:
How to deal with floating point number precision in JavaScript?
(47 answers)
Closed 8 years ago.
I was wondering if there was any way to prevent javascript from automatically rounding my numbers. I made a program to calculate pi using the Gregory-Leibniz series. It only goes to a certain amount of decimal places. Here is my code:
pi=0;
x=1;
i=1;
function doStuff(){
pi = pi+(4/x);
x=x+2;
pi = pi-(4/x);
x=x+2;
document.getElementById("someDiv").innerHTML = pi;
}
If you are trying to work with numbers requiring precision beyond the JavaScript float (only 64 bits of precision) you could consider using a library like one of those mentioned in this question: Is there a decimal math library for JavaScript?
In particular the bignumber library looks promising for your purposes.
Here is a quick demonstration jsfiddle: http://jsfiddle.net/H88tS/
Note that the fiddle is linking in the bignumber library.
$(document).ready(function () {
BigNumber.config({ DECIMAL_PLACES : 50, ERRORS : false});
var pi = new BigNumber(0, 10),
x = new BigNumber(1, 10),
two = new BigNumber(2, 10),
four = new BigNumber(4, 10);
function iterate() {
pi = pi.plus(four.dividedBy(x));
x = x.plus(two);
pi = pi.minus(four.dividedBy(x));
x = x.plus(two);
$("#pi").text(pi.toPrecision(50));
}
$('button').click(iterate);
});
Unfortunately, it's computationally impossible to prevent rounding of a number with potentially infinite decimal places.
There are some hacks one could suggest, though, like having a class HugeNumber whose objects are lists or arrays of algarisms, or even strings that contain only numbers, and having arithmetic operations implemented in it (by yourself, of course). Unless processing efficiency is a concern, this would be an acceptable solution. Maybe something like that already exists in a plugin or even in some built-in class, I just never needed that so I really don't know.
Related
I'm using this BigInteger.js for some calculations:
let myBigInt = bigInt(20).pow(200) // gets 160693804425899027554196209234116260252220299378279283530137600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
I'd like to apply the logarithm to the big integer but in the docs I could not find any matching function. How can I implement a log(baseN, valueX) function for the BigInteger.js library?
Note: let myLogarithm = myBigInt.log(baseN) is not a valid implementation.
Note: After a lot of try&error I did found a working solution my own and I will post it here because I'm pretty sure there are a few more people then me that also gots faced with the same issue right there. So I hope, I could help :)
Have a look at wikipedia, as I did because theres a very nice article about baseConversion.
Below you can find a function for Math.log(base, value) that is able to calculate the log(base) from a value.
Math.log = (function() {
var log = Math.log;
return function(base, n) {
return log(n)/(base ? log(base) : 1);
};
})();
To calculate the logarithmToBaseN for bigInt-values just use this line of code:
let logarithmToBaseN = (myBigInt.toString().length * Math.log(baseN, 10) + Math.log(baseN, parseFloat("0." + myBigInt))) - 1);
Edit: This soltuion is a tiny workaround bacause parseFloat("0." + myBigInt) converts a big value like 100000 to a really small one like 0.100000,... what causes that it will be in integer precision.
According to #Jonas W's comment: The solution is very accurate for lower bases like (5, 10, ...) combined with low values like 10, 1000, 100000 - but for really big values like bigInt(20).pow(200) is it not.
Note: Using parseFloat (IEEE 754 double precision floating-point) means, you have a maximum of 52 bits of precision, which is a bit more than 15 decimal places. After that - the accuracy will be killed.
Note: For really big values bigInt(20).pow(200) combined with really big Bases like 100*(and more) it seems to be pretty accurate again.
Greetings, jonas.
I'm dealing with relatively small and simple numbers. I first tried to do the rounding (2 signs after digit) with infamous toFixed. It's a known issue: sometimes it works incorrectly. But what struck me is the fact that it also works inconsistently:
(0.395).toFixed(2); // "0.40"
(0.295).toFixed(2); // "0.29"
These numbers are very similar, 3 signs after digit and yet different behavior.
So, I decided to switch to using Math.round. Shortly, I encountered another problem:
Math.round(0.35055 * 10000) / 100; // produces 35.05 instead of 35.06
Is Math.round also problematic? Then, what method should be used?
Unfortunately JavaScript is known to have such precision issues, issues that are better explained in the following question: Is floating point math broken?, as pointed in the comments.
If you require a greater degree of numerical accuracy, I would suggest you to use a library such as BigNumber, which also comes with its own toFixed method.
Your example would look something like this:
var a = new BigNumber('0.35055');
a = a.times(10000)
a = a.dividedBy(100)
console.log(a.toFixed(2)); //would log "35.06"
For brevity you can also chain the operations, like this: a.times(10000).dividedBy(100).toFixed(2)
I think this is working as designed. Keep in mind these numbers are stored in base 2, so there is a loss of precision when converting to and from base 10. And you have to look at these conversions if you want to understand why it looks inconsistent. If you have a fixed number of decimals that you want to keep precisely, you can use integers for operations and convert only for display.
I have a JavaScript calculator which uses the Math.cbrt() function. When I calculate the cube root of 125 it returns 4.999999999999999. I understand that I could use Math.round() to round any answers that this function returns to integer values, but I do not want to do that exactly. Is there a way to use this if and only if the result of calculation is some number followed by a string of 9s (or something similar like 4.99999998 perhaps) after the decimal?
What you are dealing with is the frustration of floating point numbers in computing. See the Floating Point Guide for more information on this critical topic.
The short version:
Certain non-integer values cannot be represented accurately by computers, so they store a value that is "near enough". Just try evaluating 3.3 / 3 in your favourite REPL.
Say what?!
Computers are supposed to be perfect at this numbers/math thing, right? Can I trust any answer they give me?
Yes, for integers, they are pretty much perfect. But for non-integer calculations, you should assume that answers won't be exact, and account for these floating point errors.
The solution in Javascript
In newer versions of Javascript, you have a defined constant Number.EPSILON, which is the smallest difference between the actual number and the approximation that it can actually store.
Using this, you can multiply that constant by the result you get and add it to the result and you should get the exact value you require.
function cbrt(n) {
return Math.cbrt(n) + (Number.EPSILON * Math.cbrt(n));
}
Alternatively, you can use the rounding behaviour of the .toFixed() method on numbers together with the parseFloat() function if you only care about numbers up to a certain number of decimal places (less than 20).
function num(n, prec) {
if (prec === void 0) prec = 8; // default to 8 decimal places
return parseFloat(n.toFixed(prec));
}
var threshold = 0.999; // set to whatever you want
var fraction = result % 1;
if (fraction >= threshold) {
result = Math.round(result);
}
My Challenge
I am presently working my way through reddit's /r/dailyprogrammer challenges using Node.js and have caught a snag. Being that I'm finishing out day 3 with this single exercise, I've decided to look for help. I refuse to just move on without knowing how.
Challenge #6: Your challenge for today is to create a program that can calculate pi accurately to at least 30 decimal places.
My Snag
I've managed to obtain the precision arithmetic I was seeking via mathjs, but am left stumped on how to obtain 30 decimal places. Does anyone know a library, workaround or config that could help me reach my goal?
/*jslint node: true */
"use strict";
var mathjs = require('mathjs'),
math = mathjs();
var i,
x,
pi;
console.log(Math.PI);
function getPi(i, x, pi) {
if (i === undefined) {
pi = math.eval('3 + (4/(2*3*4))');
i = 2;
x = 4;
getPi(i, x, pi);
} else {
pi = math.eval('pi + (4/('+x+'*'+x+1+'*'+x+2+')) - (4/('+x+2+'*'+x+3+'*'+x+4+'))');
x += 4;
i += 1;
if (x < 20000) {
getPi(i, x, pi);
} else {
console.log(pi);
}
}
}
getPi();
I have made my way through many interations of this, and in this example am using the Nilakatha Series:
This question uses some algorithm to compute digits of pi, apparently to arbitrary precision. Comments on that question indicate possible sources, in particular this paper. You could easily port that approach to JavaScript.
This algorithm has, as an alternating series, an error of about 4/n^3 if the last term is 4/((n-2)*(n-1)*n), that is, using n-3 fraction terms. To get an error smaller than 0.5*10^(-30), you would need (at least) n=2*10^10 terms of this series. With that number, you have to take care of floating point errors, especially of cancellation effects when adding a large number and a small number. The best way to avoid that is to start the summation with the smallest term and then go backwards. Or do the summation forward, but with a precision of 60 decimals, to then round the result to 30 decimals.
It would be better to use the faster converging Machin formula, or one of the Machin-like formulas, if you want to have some idea of what exactly you are computing. If not, then use one of the super fast formulas used for billions of digits, but for 30 digits this is likely overkill.
See wikipedia on the approximations of pi.
I'm attempting to perform the following calculation in Javascript:
e^x / (1 + e^x)
where x is a long floating point number.
In this instance, I require accuracy to at least the 10th decimal place.
I've been using BigDecimal to accurately handle the floating point numbers, as suggested at The Floating Point Guide.
My first attempt of:
var foo = new BigDecimal(myVeryLongFloatingPoint)
var bar = Math.exp(foo);
var spam = bar.divide(bar.add(new BigDecimal("1")));
led to the error (where xxxxx is the floating point number):
TypeError: Object xxxxx has no method 'add'
So I attempted to tried convert bar into a BigDecimal object:
var foo = new BigDecimal(myVeryLongFloatingPoint)
var bar = new BigDecimal(Math.exp(foo));
var spam = bar.divide(bar.add(new BigDecimal("1")));
which in turn leads to the error (where xxxxx is the floating point number):
BigDecimal(): Not a number: xxxxx
Where am I going wrong?
Is this a sensible approach to handling this kind of calculation with floating points where a high degree of precision is required?
You should pass strings to BigDecimal:
var bar = new BigDecimal(Math.exp(foo).toString());
There are a few mathematical approaches that might be useful. If x is positive and reasonably large, then you're taking the ratio of two large number and this will reduce your final precision. Instead, you might:
Use 1./(1. + e^(-x)) instead.
For large x, this is approximately 1.-e^(-x), and the bigger x is, the better the approximation (e.g., if x=100, then your error would be in the 86th digit).
(Honestly, one should verify this before using it, I'm just going of off memory here an not writing anything down, but if this looks useful, I could grab a pencil...)
Math.exp only works on normal Numbers and cannot operate on BigDecimals. Math.exp is probably converting foo to NaN (or something like that) before continuing.
You should look for an exponentiation method inside your BigDecimal class. I looked at the source and I think there is a BigDecimal.pow method you could use instead.