Decimal rounding in JavaScript - javascript

I am trying to achieve what was asked in this question. In most scenarios, it is working fine by using the following code:
Math.round(num * 100) / 100
But I encountered a situation where it fails due to a very odd behavior. The number I'm trying to round is 1.275. I am expecting this to be rounded to 1.28, but it is being rounded to 1.27. The reason behind this is the fact that num * 100 is resulting in 127.49999999999999 instead of 127.5.
Why is this happening and is there a way around it?
EDIT
Yes, this question is probably the root cause of the issue I'm facing, but the suggested solutions of the selected answer are not working for me. The desired end result is a rounded number that is displayed correctly. So basically I'm trying to achieve what that question is instructing (check below) but I cannot figure out how.
If you just don’t want to see all those extra decimal places: simply
format your result rounded to a fixed number of decimal places when
displaying it.

You can use toFixed to ensure the precision that you're looking for:
var x = 1.275;
var y = x * 100;
y = y.toFixed(1); // y = 127.5

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;
}
}

toFixed javascript function giving strange results?

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?

Is there a way to truncate scientific notation numbers in Javascript?

As you all know since it is one of the most asked topic on SO, I am having problems with rounding errors (it isn't actually errors, I am well aware).
Instead of explaining my point, I'll give an example of what possible numbers I have and which input I want to be able to obtain:
Let's say
var a = 15 * 1e-9;
alert(a)
outputs
1.5000000000000002e-8
I want to be able to obtain 1.5e-8 instead, but I cannot just multiply by 10e8, round and divide by 10e8 because I don't know if it will be e-8 or e-45 or anything else.
So basically I want to be able to obtain the 1.5000002 part, apply toFixed(3) and put back the exponent part.
I could convert into a string and parse but it just doesn't seem right...
Any idea ?
(I apologize in advance if you feel this is one of many duplicates, but I could not find a similar question, only related ones)
Gael
You can use the toPrecision method:
var a = 15 * 1e-9;
a.toPrecision(2); // "1.5e-8"
If you're doing scientific work and need to round with significant figures in mind: Rounding to an arbitrary number of significant digits
var a = 15 * 1e-9;
console.log(Number.parseFloat(a).toExponential(2));
//the above formula will display the result in the console as: "1.50e-8"

Problem in Javascript colors and canvas

I have used this code to exactly try to have the RGB code of color:
var huePixel = HueCanvas.css('background-color').match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);//["rgb(0, 70, 255", "0", "70", "255"]
var svPixel = SVCanvas.get(0).getContext("2d").getImageData(satPos,valPos,1,1).data;
//opacity*original + (1-opacity)*background = resulting pixel
var opacity =(svPixel[3]/255);
var r =parseInt((opacity*svPixel[0])+((1-opacity)*huePixel[1]));
var g =parseInt((opacity*svPixel[1])+((1-opacity)*huePixel[2]));
var b =parseInt((opacity*svPixel[2])+((1-opacity)*huePixel[3]));
The problem is that in some pixels , the RGB is not exactly the same . If i use Math.round than parseInt there is more problems , and more pixels have little changes than real ones.
I know that the problem is in var opacity =(svPixel[3]/255); , but i dont know how to put the equation to not have that problems.
Thanks for your attention.
I don't know the definite answer to your question (I'm not even sure I understand the question itself), but I'll take a shot.
It appears that you're trying to calculate the RGB value that you see when something else (the browser?) blends a non-opaque canvas on top of opaque background. (Are you sure this is the right thing to do at all?)
First, please don't use parseInt to round a number. It's used to parse strings and you should use it to convert huePixel[i] to an integer: parseInt(huePixel[i], 10) (note that I specify the radix explicitly to avoid numbers being parsed as octal).
To round values, you should use Math methods: Math.round (to closest integer), Math.ceil (round up) or Math.floor (round down).
Maybe the problem you're having is caused by rounding errors (hard to say without the specific inputs and expected outputs of the calculation). To minimize the rounding errors, you could try rewriting the formula like this:
(opacity * svPixel[0]) + ((1-opacity) * huePixel[1]) =
huePixel[1] + opacity * (svPixel[0]-huePixel[1]) =
huePixel[1] + svPixel[3] * (svPixel[0]-huePixel[1]) / 255

Categories

Resources