toFixed javascript function giving strange results? - javascript

I am trying to fix the number to 2 digits after decimal and for that i am using toFixedfunction of javascript. Below are the strange results i am getting, please check and help me.
var number = 11.995;
number.toFixed(2); // giving me 11.99 which is correct
var number = 19.995;
number.toFixed(2); // giving me 20.00 which is incorrect
Can anyone tell me why it is happening.
Thanks for your help.

This is how floating point math works. The value 19.995 is not exact binary (base 2). To make it more clear, think of an exact number when you divide 10/3.
For more in-depth explanations, read this: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
In your case you can work with strings instead (at least it seems like that is what you want):
number.toString().substr(0, n);
Or define a function like this (made in 2 minutes, just an example):
Number.toFixed = function(no, n) {
var spl = no.toString().split('.');
if ( spl.length > 1 ) {
return spl[0]+'.'+spl[1].substr(0,n);
}
return spl[0];
}
Number.toFixed(19.995, 2); // 19.99

toFixed rounds the value. Since 19.995 is exactly halfway between 19.99 and 20.00, it has to choose one of them. Traditionally, rounding prefers the even result (this prevents bias, since round-ups and round-downs will be equal).

I have create a function which done all for me..
function toFixed(number, precision) {
var multiplier = Math.pow(10, precision + 1),
wholeNumber = Math.floor(number * multiplier);
return Math.round(wholeNumber / 10) * 10 / multiplier;
}
//Call this function to retrive exect value
toFixed((+adjustmentval), 2);

David has answered your doubt I'm guessing. Just providing an alternate solution here.
You can use the Math.floor() method of the Math object for this.
Something like this, Math.floor(number*100)/100

Can anyone tell me why it is happening.
The IEEE-754 double-precision binary floating point number standard used by JavaScript's number type (and similar times in several other languages) does not perfectly store all numbers, it stores some numbers imprecisely, in a way that lets it A) Store them in just 64 bits, and B) Calculate with them quickly.
For 11.995, the actual value is 11.99499988555908203125, just slightly less than 11.995.
For 19.995, the actual value is 19.9950008392333984375, just slightly more than 19.995.
That explains why when you round them using the usual round-to-nearest-half-up operation, 11.995 (which is really 11.99499988555908203125) rounds down to 11.99 but 19.995 (which is really 19.9950008392333984375) rounds up to 20.00.
(This site has a handy calculator for visualizing this stuff.)
More here on SO:
Is floating point math broken?
How to deal with floating point number precision in JavaScript?

Related

Javascript error in toPrecision() function

I want to use toPrecision() to reduce the size of a number before I display it. However, I sometimes cannot multiply output of the function by another number without gaining a small rounding error. See the code sample below:
var x = 0.0197992182093305
alert (x.toPrecision(4)) //Correct: 0.01980
alert (Number(x.toPrecision(4))) //Correct: 0.0198
alert( Number(x.toPrecision(4)) * 100) //Incorrect: 1.9800000000000002
JSFiddle: http://jsfiddle.net/ueLsL460/4/ What's going on here?
Based on what I understand, Number(x.toPrecision(4)) * 100 creates a new Number object which will not inherit the precision of the parent.
If you still want it to be precise after Math, you need to put it in precision again.
alert((x * 100).toPrecision(4));
Technically, it's not an error. It's just the way javascript is supposed to work.
The use of primitive constructors is not that ideal, unless you are trying to do something trivial. Can you please try to do the code on the following fiddle and see if this will do for you?
http://jsfiddle.net/ueLsL460/5/
var x = 0.0197992182093305;
alert((x * 100).toPrecision(4));
Javascript doesn't have a decimal equivalent - so in your case floating points are used. What this means is that Number(x.toPrecision(4)) doesn't give you exactly 0.0198, but something the FP binary number closest to 0.0198. So any arithmetic you do will be subject to the loss of precision introduced in floating point arithmetic. You can see the same effect if you do
alert(0.0198 * 100);
By the way
alert(0.0198 * 10 * 10);
gives you no problem (the loss of precision does not build up enough to make it into the digits Javascript deems to display) - but that's for this particular number alone.

Javascript .toFixed() more precise rounding alternative?

For rounding decimals (prices), I've been using .toFixed(2) for quite some time now. But I just recently discovered that Javascript can't "precisely" round decimals. I was a bit shocked that even 10.005 couldn't be rounded correctly to 10.01. It just got rounded down to 10.00. And other times it did round correctly. I like to have control over my code, so this is a big no-no for me.
And since I'm calculating prices, I think I need something more (100%) accurate for rounding only 2- or 3-decimal numbers, maybe a 4-decimal one.
Is there no straightforward way of doing basic rounding in javascript, the correct way?
UPDATE: As Felix Kling has suggested, the method of processing my prices as integers of cents, there are also drawbacks to this (besides more code)?
The reason that a number like 10.005 can't be rounded corretly is that you don't really have the number 10.005, you only have a number that is the closest possible one that can be represented using a double precision floating point variable.
The actual number that you have might be someting like 10.00499999999276253, and that would naturally round to 10.00 rather than 10.01.
To handle monetary values you should use a data type that can represent the value exactly. As numbers in Javascript are always floating point numbers, what you are left with is representing the numbers as text, and writing your own functions to do the math (or find someone who has done that already).
This should work for you, here's a fiddle to play around with it http://jsfiddle.net/5ffyC/1/
function moneyRound(flt){
var splitStr = flt.toString().split('.'),
whole = (flt * 100) | 0;
if (splitStr.length > 1 && splitStr[1].length > 2){
return splitStr[1][2] > 4? (whole + 1) / 100: whole / 100;
} else {
return flt;
}
}

Javascript issue with math calculations

Why is it if I do this in javascript, I get the following result:
1234.56 * 10 = 12345.599999999999
It should be 123456. How can I get around this problem?
Thanks.
Floating points are not exact, since there are ifinite numbers at their range [or in any range to be more exact], and only a finite number of bits to store this data.
Have a look at what every programmer should know about floating point arithmetics.
Another easy solution:
parseFloat((1234.56 * 10).toPrecision(12))
and the result will be: 12345.6, and YES... it works with decimal numbers.
As the others said, floating points and so on.
Easy solution would be to do something like this:
var answer = parseInt(1234.56 * 10);
Or just use Math.round?
All numbers in JS are internally defined by float and drop the less significant digits if needed.
(10000000000000000000000000000 + 1) == 10000000000000000000000000000
// this will return true
And javascript is well known for droping bits quite often in numbers. So handle with care

32.48 * 10 = 324.79999999999995 in javascript calculations

var quantity = $(this).find('td:eq(2) input').val()*1;
var unitprice = $(this).find('td:eq(3) input').val()*1;
var totaltax = 0;
$(this).find('td:eq(4) input[name^=taxamount]').each(function(){
totaltax = (totaltax*1)+($(this).val()*1);
});
var subtotal = (unitprice+totaltax);
alert(subtotal+' is unit subtotal, to mulitply by '+quantity);
var total = subtotal*quantity;
$(this).find('td:last').html('$'+total);
In this case, based on my DOM, the results are all integers (especially because I'm making sure I apply the *1 modifier to values to ensure they are numbers, not strings).
In this case, these are teh values returned within the first 7 lines of the above code (and verified through alert command)
quantity: 10
unitprice: 29
totaltax: 3.48
subtotal = 32.48
When I multiply subtotal*quantity for the total variable, total returns:
total: 324.79999999999995
So at the end, I get the td:last filled with $324.79999999999995 rather than $324.80 which would be more correct.
Bizarre, I know. I tried all sorts of alerts at different points to ensure there were no errors etc.
This has been asked one bizillion times.
Please read: What Every Computer Scientist Should Know About Floating-Point Arithmetic
You're coming up against a familiar issue with floating point values: certain values can't be precisely represented in a finite binary floating point number.
See here:
How to deal with floating point number precision in JavaScript?
This is the way floating point numbers work. There's nothing bizarre going on here.
I'd recommend that you round the value appropriately for display.
That's the joy of floating point arithmetic -- some base 10 decimals cannot be represented in binary.
http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
Computers can't handle decimals very well in binary since in real mathematics there are literally an infinite number of values between 0.01 and 0.02 for example. So they need to store approximations, and when you do arithmetic on those approximations the results can get a little away from the true result.
You can fix it with (Math.round(total*100)/100).toFixed(2);
As others have mentioned, this is the way its meant to work. A suggested workaround can be found below:
var v = "324.32999999999995";
function roundFloat(n, d) {
var a= Math.pow(10, d);
var b= Math.round(n * a) / a;
return b;
}
$("body").append(roundFloat(v,3));
Where v would be replaced with the desired value.
You can view the working example at: http://jsfiddle.net/QZXhc/
You could try rounding to 2 decimal digits as workaround

Precise Financial Calculation in JavaScript. What Are the Gotchas?

In the interest of creating cross-platform code, I'd like to develop a simple financial application in JavaScript. The calculations required involve compound interest and relatively long decimal numbers. I'd like to know what mistakes to avoid when using JavaScript to do this type of math—if it is possible at all!
You should probably scale your decimal values by 100, and represent all the monetary values in whole cents. This is to avoid problems with floating-point logic and arithmetic. There is no decimal data type in JavaScript - the only numeric data type is floating-point. Therefore it is generally recommended to handle money as 2550 cents instead of 25.50 dollars.
Consider that in JavaScript:
var result = 1.0 + 2.0; // (result === 3.0) returns true
But:
var result = 0.1 + 0.2; // (result === 0.3) returns false
The expression 0.1 + 0.2 === 0.3 returns false, but fortunately integer arithmetic in floating-point is exact, so decimal representation errors can be avoided by scaling1.
Note that while the set of real numbers is infinite, only a finite number of them (18,437,736,874,454,810,627 to be exact) can be represented exactly by the JavaScript floating-point format. Therefore the representation of the other numbers will be an approximation of the actual number2.
1 Douglas Crockford: JavaScript: The Good Parts: Appendix A - Awful Parts (page 105).
2 David Flanagan: JavaScript: The Definitive Guide, Fourth Edition: 3.1.3 Floating-Point Literals (page 31).
Scaling every value by 100 is the solution. Doing it by hand is probably useless, since you can find libraries that do that for you. I recommend moneysafe, which offers a functional API well suited for ES6 applications:
const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8
https://github.com/ericelliott/moneysafe
Works both in Node.js and the browser.
There's no such thing as "precise" financial calculation because of just two decimal fraction digits but that's a more general problem.
In JavaScript, you can scale every value by 100 and use Math.round() everytime a fraction can occur.
You could use an object to store the numbers and include the rounding in its prototypes valueOf() method. Like this:
sys = require('sys');
var Money = function(amount) {
this.amount = amount;
}
Money.prototype.valueOf = function() {
return Math.round(this.amount*100)/100;
}
var m = new Money(50.42355446);
var n = new Money(30.342141);
sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76
That way, everytime you use a Money-object, it will be represented as rounded to two decimals. The unrounded value is still accessible via m.amount.
You can build in your own rounding algorithm into Money.prototype.valueOf(), if you like.
Unfortunately all of the answers so far ignore the fact that not all currencies have 100 sub-units (e.g., the cent is the sub-unit of the US dollar (USD)). Currencies like the Iraqi Dinar (IQD) have 1000 sub-units: an Iraqi Dinar has 1000 fils. The Japanese Yen (JPY) has no sub-units. So "multiply by 100 to do integer arithmetic" isn't always the correct answer.
Additionally for monetary calculations you also need to keep track of the currency. You can't add a US Dollar (USD) to an Indian Rupee (INR) (without first converting one to the other).
There are also limitations on the maximum amount that can be represented by JavaScript's integer data type.
In monetary calculations you also have to keep in mind that money has finite precision (typically 0-3 decimal points) & rounding needs to be done in particular ways (e.g., "normal" rounding vs. banker's rounding). The type of rounding to be performed might also vary by jurisdiction/currency.
How to handle money in javascript has a very good discussion of the relevant points.
In my searches I found the dinero.js library that addresses many of the issues wrt monetary calculations. Haven't used it yet in a production system so can't give an informed opinion on it.
use decimaljs ... It a very good library that solves a harsh part of the problem ...
just use it in all your operation.
https://github.com/MikeMcl/decimal.js/
Your problem stems from inaccuracy in floating point calculations. If you're just using rounding to solve this you'll have greater error when you're multiplying and dividing.
The solution is below, an explanation follows:
You'll need to think about mathematics behind this to understand it. Real numbers like 1/3 cannot be represented in math with decimal values since they're endless (e.g. - .333333333333333 ...). Some numbers in decimal cannot be represented in binary correctly. For example, 0.1 cannot be represented in binary correctly with a limited number of digits.
For more detailed description look here: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Take a look at the solution implementation: http://floating-point-gui.de/languages/javascript/
Due to the binary nature of their encoding, some decimal numbers cannot be represented with perfect accuracy. For example
var money = 600.90;
var price = 200.30;
var total = price * 3;
// Outputs: false
console.log(money >= total);
// Outputs: 600.9000000000001
console.log(total);
If you need to use pure javascript then you have need to think about solution for every calculation. For above code we can convert decimals to whole integers.
var money = 60090;
var price = 20030;
var total = price * 3;
// Outputs: true
console.log(money >= total);
// Outputs: 60090
console.log(total);
Avoiding Problems with Decimal Math in JavaScript
There is a dedicated library for financial calculations with great documentation. Finance.js
Use this code for currency calculation and round numbers in two digits.
<!DOCTYPE html>
<html>
<body>
<h1>JavaScript Variables</h1>
<p id="test1"></p>
<p id="test2"></p>
<p id="test3"></p>
<script>
function setDecimalPoint(num) {
if (isNaN(parseFloat(num)))
return 0;
else {
var Number = parseFloat(num);
var multiplicator = Math.pow(10, 2);
Number = parseFloat((Number * multiplicator).toFixed(2));
return (Math.round(Number) / multiplicator);
}
}
document.getElementById("test1").innerHTML = "Without our method O/P is: " + (655.93 * 9)/100;
document.getElementById("test2").innerHTML = "Calculator O/P: 59.0337, Our value is: " + setDecimalPoint((655.93 * 9)/100);
document.getElementById("test3").innerHTML = "Calculator O/P: 32.888.175, Our value is: " + setDecimalPoint(756.05 * 43.5);
</script>
</body>
</html>

Categories

Resources