Useless decimals in calculation - javascript

Might somebody already faced with calculation issue like in snippet below. First console command shows useless digits after calculation 10.1 +20.1 should be 30.2 but calculated as 30.200000000000003. and second one 10.2 + 20.2=30.4 is correct. There can be a lot of such decimal pairings which creates additional decimals in summ. Why does it happends and hove to avoid it?
console.log(10.1 + 20.1);
console.log(10.2 + 20.2);

This looks like a duplicate of How to deal with floating point number precision in JavaScript?.
You can use .toFixed(2) to always get a number with only 2 decimals. https://www.w3schools.com/jsref/jsref_tofixed.asp
More info: https://www.w3schools.com/js/js_numbers.asp

Related

Rounding up or down when 0.5

I am having an issue with the way Javascript is rounding numbers when hitting 0.5.
I am writing levies calculators, and am noticing a 0.1c discrepancy in the results.
The problem is that the result for them is 21480.705 which my application translates into 21480.71, whereas the tariff says 21480.70.
This is what I am seeing with Javascript:
(21480.105).toFixed(2)
"21480.10"
(21480.205).toFixed(2)
"21480.21"
(21480.305).toFixed(2)
"21480.31"
(21480.405).toFixed(2)
"21480.40"
(21480.505).toFixed(2)
"21480.51"
(21480.605).toFixed(2)
"21480.60"
(21480.705).toFixed(2)
"21480.71"
(21480.805).toFixed(2)
"21480.81"
(21480.905).toFixed(2)
"21480.90"
Questions:
What the hell is going on with this erratic rouding?
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
So as some of the others already explained the reason for the 'erratic' rounding is a floating point precision problem. You can investigate this by using the toExponential() method of a JavaScript number.
(21480.905).toExponential(20)
#>"2.14809049999999988358e+4"
(21480.805).toExponential(20)
#>"2.14808050000000002910e+4"
As you can see here 21480.905, gets a double representation that is slightly smaller than 21480.905, while 21480.805 gets a double representation slightly larger than the original value. Since the toFixed() method works with the double representation and has no idea of your original intended value, it does all it can and should do with the information it has.
One way to work around this, is to shift the decimal point to the number of decimals you require by multiplication, then use the standard Math.round(), then shift the decimal point back again, either by division or multiplication by the inverse. Then finally we call toFixed() method to make sure the output value gets correctly zero-padded.
var x1 = 21480.905;
var x2 = -21480.705;
function round_up(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd); // Or you can just use 1/rup
return (Math.round(x*rup)*rdwn).toFixed(nd)
}
function round_down(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd);
return (Math.round(x*-rup)*-rdwn).toFixed(nd)
}
function round_tozero(x,nd)
{
return x>0?round_down(x,nd):round_up(x,nd)
}
console.log(x1,'up',round_up(x1,2));
console.log(x1,'down',round_down(x1,2));
console.log(x1,'to0',round_tozero(x1,2));
console.log(x2,'up',round_up(x2,2));
console.log(x2,'down',round_down(x2,2));
console.log(x2,'to0',round_tozero(x2,2));
Finally:
Encountering a problem like this is usually a good time to sit down and have a long think about wether you are actually using the correct data type for your problem. Since floating point errors can accumulate with iterative calculation, and since people are sometimes strangely sensitive with regards to money magically disappearing/appearing in the CPU, maybe you would be better off keeping monetary counters in integer 'cents' (or some other well thought out structure) rather than floating point 'dollar'.
The why -
You may have heard that in some languages, such as JavaScript, numbers with a fractional part are calling floating-point numbers, and floating-point numbers are about dealing with approximations of numeric operations. Not exact calculations, approximations. Because how exactly would you expect to compute and store 1/3 or square root of 2, with exact calculations?
If you had not, then now you've heard of it.
That means that when you type in the number literal 21480.105, the actual value that ends up stored in computer memory is not actually 21480.105, but an approximation of it. The value closest to 21480.105 that can be represented as a floating-point number.
And since this value is not exactly 21480.105, that means it is either slightly more than that, or slightly less than that. More will be rounded up, and less will be rounded down, as expected.
The solution -
Your problem comes from approximations, that it seems you cannot afford. The solution is to work with exact numbers, not approximate.
Use whole numbers. Those are exact. Add in a fractional dot when you convert your numbers to string.
This works in most cases. (See note below.)
The rounding problem can be avoided by using numbers represented in
exponential notation:
function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
console.log(round(21480.105, 2).toFixed(2));
Found at http://www.jacklmoore.com/notes/rounding-in-javascript/
NOTE: As pointed out by Mark Dickinson, this is not a general solution because it returns NaN in certain cases, such as round(0.0000001, 2) and with large inputs.
Edits to make this more robust are welcome.
You could round to an Integer, then shift in a comma while displaying:
function round(n, digits = 2) {
// rounding to an integer is accurate in more cases, shift left by "digits" to get the number of digits behind the comma
const str = "" + Math.round(n * 10 ** digits);
return str
.padStart(digits + 1, "0") // ensure there are enough digits, 0 -> 000 -> 0.00
.slice(0, -digits) + "." + str.slice(-digits); // add a comma at "digits" counted from the end
}
What the hell is going on with this erratic rouding?
Please reference the cautionary Mozilla Doc, which identifies the cause for these discrepancies. "Floating point numbers cannot represent all decimals precisely in binary which can lead to unexpected results..."
Also, please reference Is floating point math broken? (Thank you Robby Cornelissen for the reference)
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
Use a JS library like accounting.js to round, format, and present currency.
For example...
function roundToNearestCent(rawValue) {
return accounting.toFixed(rawValue, 2);
}
const roundedValue = roundToNearestCent(21480.105);
console.log(roundedValue);
<script src="https://combinatronics.com/openexchangerates/accounting.js/master/accounting.js"></script>
Also, consider checking out BigDecimal in JavaScript.
Hope that helps!

Better way of rounding off or rounding up in javascript

I have this problem with rounding up or rounding off with javascript. Well it gets the job done but there are some scenarios that it fails to do the job properly.
For example.
The price is 0.30 with a 15% discount should have 0.255 or 0.26 as result. (this is somehow okay)
The price is 79.99 with a 50% discount should have 40 as result. (actual result is 39.995)
Tried using this as reference Using toFixed(2) and math round to get correct rounding
also did this process Math.round(value*Math.pow(10,dec))/Math.pow(10,dec);
Any ideas?
Thanks
This is my initial test. Though this is just an initial test what I did is checked if the last two digits is greater than 5 then apply Math.ceil that would to the 0.255 to be 0.26 but when using 39.99 the result would still be 39.99 though 39.99 should use Math.round so that the result would be 40.
What I would like to accomplish is to be able to round up or round down any number if it needs rounding up or if it needs rounding up.
UPDATE
I was able to solve my own problem. (Atleast to my specific environment)
http://jsfiddle.net/xMj3M/4/
This is because of how floats are handled. I would suggest having your original numbers in integers only, so you would start with 7999. If you use this method, you will get the correct result.
In general, especially when dealing with money, it is always a good idea to work in pennies first, and then output it in the format of the currency.
You could try:
var someNumber = 36.7327;
var someNumberRoundedTo2Decimals = (Math.round((someNumber * 100)) / 100);
// = 36.73
basically this moves the decimal to the right 2 digits..
3673.27
then rounds it off to an int
3673
then moves the decimal back to the left 2 places
36.73

Javascript multiplying incorrectly, causing incorrect rounding

When I pull the values I want to multiply, they're strings. So I pull them, parse them as floats (to preserve the decimal places), and multiply them together.
LineTaxRate = parseFloat(myRate) * parseFloat(myQuantity) * parseFloat(myTaxRateRound);
This has worked for 99% of my invoices but I discovered one very odd problem.
When it multiplied: 78 * 7 * 0.0725
Javascript is returning: 39.584999999999994
When you normally do the math in a calculator its: 39.585
When all is said and done, I take that number and round it using .toFixed(2)
Because Javascript is returning that number, it's not rounding to the desired value of: $39.59
I tried Math.round() the total but I still get the same number.
I have thought of rounding the number to 3 decimals then two, but that seems hacky to me.
I have searched everywhere and all I see is people mention parseFloat loses its precision, and to use .toFixed, however in the example above, that doesn't help.
Here is my test script i made to recreate the issue:
<script>
var num1 = parseFloat("78");
var num2 = parseFloat("7");
var num3 = parseFloat("0.0725");
var myTotal = num1 * num2 * num3;
var result = Math.round(myTotal*100)/100
alert(myTotal);
alert(myTotal.toFixed(2));
alert(result);
</script>
Floating points are represented in binary, not decimal. Some decimal numbers will not be represented precisely. And unfortunately, since Javascript only has one Number class, it's not a very good tool for this job. Other languages have decent decimal libraries designed to avoid precisely this kind of error. You're going to have to either accept one-cent errors, implement a solution server-side, or work very hard to fix this.
edit: ooh! you can do 78 * 7 * 725 and then divide by 10000, or to be even more precise just put the decimal point in the right place. Basically represent the tax rate as something other than a tiny fraction. Less convenient but it'll probably take care of your multiplication errors.
You might find the Accounting.js library useful for this. It has an "improved" toFixed() method.
JavaScript/TypeScript have only one Number class which is not that good. I have the same problem as I am using TypeScript. I solved my problem by using decimal.js-light library.
new Decimal(78).mul(7).mul(0.0725) returns as expected 39.585

Javascript calculation gives too many zero digits

I think it's time get some second opinions on the javascript I'm working on at the moment.
I'm trying to create a calculator to tell people if they can save money by using a subscription etc etc. The calculation itself is done by som inputs from the client and some prevalues from Umbraco CMS.
The result I get from the calculator at the moment is "right" in that the actual numbers are correct, but there's just too many zeroes in it.
The calculator can be found here: my calculator
The test data I'm using is the following:
Antal ansatte: 4
Gennemsnitlig ordreværdi kr. (ca.): 400
Antal ordrer årligt (ca.): 5500
Overskudsgrad (hvad er det?): 2.7
Which gives the output: 712800.0000000001
I tried to divide this by 100 and, of course, it just moved the comma/dot two steps the left. Still there's all those zeroes.
Any help/hint is greatly appreciated! :-)
Thanks in advance,
Bo
P.S. The result I'm looking for would be 7128.00
I believe you want to call toFixed on your integer.
(712800.00000001/100).toFixed(2)
"7128.00"
It will change your number into a string, but if it is for display purposes it should be fine.
You can do something like this:
value.toFixed(2);
This will round off all your trailing decimals to only 2.
You may want to use the round() method like this:
var val = Math.round(val*100)/100
This will give you the 2-decimal place precision you want, with rounding when needed.
Try this:
http://www.mredkj.com/javascript/nfbasic2.html
It is Number Format code for decimal places.
I suspect that you're running into floating point rounding errors (Is floating point math broken?). Try the solutions presented in Is there a definitive solution to javascript floating-point errors?.

Why does adding two decimals in Javascript produce a wrong result? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Is JavaScript’s Math broken?
Why does JS screw up this simple math?
console.log(.1 + .2) // 0.3000000000000004
console.log(.3 + .6) // 0.8999999999999999
The first example is greater than the correct result, while the second is less. ???!! How do you fix this? Do you have to always convert decimals into integers before performing operations? Do I only have to worry about adding (* and / don't appear to have the same problem in my tests)?
I've looked in a lot of places for answers. Some tutorials (like shopping cart forms) pretend the problem doesn't exist and just add values together. Gurus provide complex routines for various math functions or mention JS "does a poor job" in passing, but I have yet to see an explanation.
It's not a JS problem but a more general computer one. Floating number can't store properly all decimal numbers, because they store stuff in binary
For example:
0.5 is store as b0.1
but 0.1 = 1/10 so it's 1/16 + (1/10-1/16) = 1/16 + 0.0375
0.0375 = 1/32 + (0.0375-1/32) = 1/32 + 00625 ... etc
so in binary 0.1 is 0.00011...
but that's endless.
Except the computer has to stop at some point. So if in our example we stop at 0.00011 we have 0.09375 instead of 0.1.
Anyway the point is, that doesn't depend on the language but on the computer. What depends on the language is how you display numbers. Usually, the language rounds numbers to an acceptable representation. Apparently JS doesn't.
So what you have to do (the number in memory is accurate enough) is just to tell somehow to JS to round "nicely" number when converting them to text.
You may try the sprintf function which give you a fine control of how to display a number.
From The Floating-Point Guide:
Why don’t my numbers, like 0.1 + 0.2
add up to a nice round 0.3, and
instead I get a weird result like
0.30000000000000004?
Because internally, computers use a
format (binary floating-point) that
cannot accurately represent a number
like 0.1, 0.2 or 0.3 at all.
When the code is compiled or
interpreted, your “0.1” is already
rounded to the nearest number in that
format, which results in a small
rounding error even before the
calculation happens.
The site has detailed explanations as well as information on how to fix the problem (and how to decide whether it is a problem at all in your case).
This is not a javascript only limitation, it applies to all floating point calculations. The problem is that 0.1 and 0.2 and 0.3 are not exactly representable as javascript (or C or Java etc) floats. Thus the output you are seeing is due to that inaccuracy.
In particular only certain sums of powers of two are exactly representable. 0.5 = =0.1b = 2^(-1), 0.25=0.01b=(2^-2), 0.75=0.11b = (2^-1 + 2^-2) are all OK. But 1/10 = 0.000110001100011..b can only be expressed as an infinite sum of powers of 2, which the language chops off at some point. Its this chopping that is causing these slight errors.
This is normal for all programming languages because not all decimal values can be represented exactly in binary. See What Every Computer Scientist Should Know About Floating-Point Arithmetic
It has to do with how computers handle floating numbers. You can read more about it here: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Categories