Rounding in JS: inconsistent and unexpected behaivior - javascript

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.

Related

Modulo - calculations error

Let me be brief. I'm trying to calculate
alert((Math.pow(7,35))%71)
but it gives me 61, when the result must be 70. What's wrong?
As others mentioned before with regards to using Math.pow(7,35), this resulting number is way too big for Javascript to handle.
To resolve your problem you need to use an external javascript library.
(Or write your own ;) )
Here are some examples of Javascript libraries that handle big numbers.
BigNum
Bignumber
I hope it helps.
The number you're using is too big for javascript. The max size of an int is 2^53 -- which is less than 7^35.
The only value which requires more precision is an intermediate result. So the problem can also be avoided without the need for higher precision variables when you have an algorithm that doesn't need the problematic intermediate result.
The following formula can be useful for that:
(a.b) % c = (a % c)(b % c) % c
This means Math.pow(7,35)%71 = ((Math.pow(7,17)%71) * (Math.pow(7,18)%71)) % 71.
Now the intermediate results are smaller, but might still be too big. So we need to split up further and to apply the modula operator on smaller intermediate results.
So you can do something like this:
Math.pow((Math.pow(7,7)%71),5)%71
But you probably need to do this for integer numbers wich are variable (otherwise, you could have avoided the problem by hardcoding the result).
So, you need to have an idea about the range of values you can expect and to define an algoritm that splits up the power calculation into pieces that will always have results that are small enough when the input is within the expected range.
And whatever you choose for a calculation like this, calculation with higher precision variables (using a specialized library) or a specilized algoritm, you should always assert the input to make sure your calculation returns a value only when you are sure it can deliver a correct value. Otherwise your implementation becomes unreliable for anyone who isn't aware of the range in which it is valid. Otherwise return an exception with a message that tells clearly which input or combination of inputs is the problem and why.

Is it possible to achieve arbitrary-precision arithmetic with no rounding issues in JavaScript?

I've tried big.js, bignumber.js, and decimal.js; they all work reasonably well up to a certain point, but fall short when I need to do arbitrary-precision calculations with large enough numbers of "odd" digits (my current test case is 31435517643980 * (1 / 31435517643980) === 1). I am open to any solution that allows me to process expressions like this, including calls to an external API. I'm currently looking at Wolfram|Alpha's API, but the 2000 calls/month limit is a restriction I'd like to avoid, because my application is going to be making quite a few calls.
If this is the wrong SE site for this question, please let me know and/or move it.
Possibly the most common way to do this is simply multiply both numbers by the same multiplier to make them have no decimals, and then do the operation, then divide again. Here's a crude implementation:
function getDigits(n){
return n.toString().substring(n.toString().indexOf('.')+1).length;
}
function xNums(n1,n2){
var highRes=(n1*Math.pow(10,getDigits(n1))*(n2*Math.pow(10,getDigits(n2))));
return highRes/Math.pow(10,getDigits(n1))/Math.pow(10,getDigits(n2));
}
Then, run xNums(31435517643980,(1 / 31435517643980))===1. Works for me in Chrome

Number.prototype.toFixed: amazingly correct in Internet Explorer

Consider the following:
var x = 2.175;
console.log(x.toFixed(2)); // 2.17
What? No, no surprise here. That's rather obvious, see: Number literal 2.175 is actually stored in memory (by IEEE-754 rules) as a value that's just a tiny little bit smaller than 2.175. And that's easy to prove:
console.log(x.toFixed(20)); // 2.17499999999999982236
That's how it works in the latest versions of Firefox, Chrome, and Opera on 32-bit Windows setup. But that's not the question.
The real question is how Internet Explorer 6 (!) actually manages to do it right as humans do:
var x = 2.175;
console.log(x.toFixed(2)); // 2.18
console.log(x.toFixed(20)); // 2.17500000000000000000
OK, I overdramatized: actually all Internet Explorers I tested this on (IE8-11, and even MS Edge!) behave the same way. Still, WAT?
UPDATE: It gets stranger:
x=1.0;while((x-=0.1) > 0) console.log(x.toFixed(20));
IE Chrome
0.90000000000000000000 0.90000000000000002220
0.80000000000000000000 0.80000000000000004441
0.70000000000000010000 0.70000000000000006661
0.60000000000000010000 0.60000000000000008882
0.50000000000000010000 0.50000000000000011102
0.40000000000000013000 0.40000000000000013323
0.30000000000000015000 0.30000000000000015543
0.20000000000000015000 0.20000000000000014988
0.10000000000000014000 0.10000000000000014433
0.00000000000000013878 0.00000000000000013878
Why the difference - in all but the last one? And why no difference in the last one? It's very similar for x=0.1; while(x-=0.01)..., by the way: until we get very close to zero, toFixed in IE apparently attempts to cut some corners.
Disclaimer: I do know that floating-point math is kinda flawed. What I don't understand is what's the difference between IE and the rest of the browser's world.
The reported behavior deviates from the requirements of the ECMA specification.
Per clause 8.5, the Number type has the IEEE-754 64-bit binary values, except there is only one NaN. So 2.175 cannot be represented exactly; the closest you can get is 2.17499999999999982236431605997495353221893310546875.
Per 15.7.4.5, toFixed(20) uses an algorithm that boils down to:
“Let n be an integer for which the exact mathematical value of n ÷ 10f – x is as close to zero as possible. If there are two such n, pick the larger n.”
In the above, f is 20 (the number of digits requested), and x is the operand, which should be 2.17499999999999982236431605997495353221893310546875.
This results in selecting 217499999999999982236 for n.
Then n is formatted, producing “2.17499999999999982236”.
I appreciate Eric's contribution, but, with all due respect, it doesn't answer the question. I admit I was too tongue-in-cheeky with those 'right' and 'amazingly correct' phrases; but yes, I understand that IE behavior is a deviation actually.
Anyway. I was still looking for an explanation what causes IE to behave differently - and I finally got something looking like a clue... ironically, in Mozilla's tracker, in this lengthy discussion. Quote:
OUTPUT IN MOZILLA:
a = 0.827 ==> a.toFixed(17) = 0.82699999999999996
b = 1.827 ==> b.toFixed(17) = 1.82699999999999996
OUTPUT IN IE6:
a = 0.827 ==> a.toFixed(17) = 0.82700000000000000
b = 1.827 ==> b.toFixed(17) = 1.82700000000000000
The difference seen in IE and Mozilla is as follows. IE is storing 'a' as a string
and Mozilla is storing 'a' as a value. The spec doesn't nail down the
storage format. Thus when IE does a.toFixed it starts out with a
exact string representation while Mozilla suffers the round trip conversions.
Would be great to have kind of official confirmation on this, but at least that explains everything I have seen yet. In particular,
console.log( 0.3.toFixed(20) ); // 0.30000000000000000000
console.log( 0.2.toFixed(20) ); // 0.20000000000000000000
console.log( (0.3 - 0.2).toFixed(20) ); // "0.09999999999999998000"
First of all, Floating points are can not be "precisely" represented in binary numbers. There will be an elevation/depression, either the value will be a little higher or a little lower. How much that is elevated/depressed depends on how the conversion is done. There is not exactly a "right value" even for a string output of ECMAScript's toFixed().
But the ECMA Standards do spice things up by er.. setting standards. Which is a good thing in my opinion. It's like "If we are all gonna make mistakes anyway, let's make the same one."
So, the question now would be, how and why does IE deviates from the Standards. Let's examine the following test cases.
Candidates are IE 10.0.9200.16688 and Chrome 30.0.1599.69, running on x64 Windows 8 Pro.
Case Code IE (10) Chrome (30)
--------------------------------------------------------------------------------
A (0.06).toFixed(20) 0.60000000000000000000 0.05999999999999999778
B (0.05+0.01).toFixed(20) 0.06000000000000000500 0.06000000000000000472
So, regardless it's IE or Chrome, we see (0.06) is not exactly equal to (0.05+0.01). Why is that? It's because (0.06) has a representation that is very close to but not equal to (0.06), so does (0.05) and (0.01). When we perform an operation, such as an addition, the very less significant errors can sum up to become an error of slightly different magnitude.
Now, the difference in represented value in different browsers can be impacted due to 2 reasons:
The conversion algorithm used.
When the conversion takes place.
Now we don't know what algo IE uses since I can't look into it's source. But the above test cases clearly demonstrate one other thing, IE and Chrome handles the conversion "not only differently" but also "on a different occasion".
In JavaScript, when we create a number (aka instance of a Number class with or without the new keyword), we actually provide a literal. The literal is always a string even if it denotes a number [1]. The browser parses the literal and creates the object and assigns the represented value.
Now, here's where things tend to go different ways. IE holds off the conversion until it is needed. That means until an operation takes place, IE keeps the number as literal (or some intermediary format). But Chrome converts it rightaway in the operational format.
After the operation is done, IE does not revert back to the literal format or the intermediary format, as it is pointless and may cause a slight loss in precision.
I hope that clarifies something.
[1] Value represented in code are always literals. If you quote them they are called String Literals.

Using Math.exp() in Javascript with BigDecimal for large floating point numbers

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.

Javascript - How to squared a number?

Using the javascript function
function squareIt(number) {
return number * number;
}
When given the number 4294967296 the function returns 18446744073709552000 is returned
Everyone knows the real answer is 18446744073709551616 :-)
I guess this is to to with rounding on my 32-bit machine. However, would this script give the right answer on a 64 bit machine? Has anyone tried this?
ChrisV- see this post. Also it easier for people to evaluate your question by typing the following JavaScript directly into the browser URL textbox:
javascript:4294967296*4294967296
what about this
function squareIt(number){
return Math.pow(number,2)
}
Javascript uses 64 bit floating point arithmetic internally for numerical calculations - the results you see are a reflection of this, and will happene regardless of the underlying architecture.
Here is one more example based on BigInteger.js.
alert(bigInt(4294967296).square());
<script src="https://cdnjs.cloudflare.com/ajax/libs/big-integer/1.6.40/BigInteger.min.js"></script>

Categories