Sudarshan
Sudarshan

Reputation: 18534

Math.round Rounding Error

I Want to round 1.006 to two decimals expecting 1.01 as output

When i did

var num = 1.006;
alert(Math.round(num,2)); //Outputs 1 
alert(num.toFixed(2)); //Output 1.01

Similarly,

var num =1.106;
alert(Math.round(num,2)); //Outputs 1
alert(num.toFixed(2));; //Outputs 1.11

So

Please suggest me.

P.S: I tried searching stack overflow for similar answers, but could not get proper answer.

EDIT:

Why does 1.015 return 1.01 where as 1.045 returns 1.05

var num =1.015;
alert(num.toFixed(2)); //Outputs 1.01
alert(Math.round(num*100)/100); //Outputs 1.01

Where as

var num = 1.045;
alert(num.toFixed(2)); //Outputs 1.04
alert(Math.round(num*100)/100); //Outputs 1.05

Upvotes: 7

Views: 7654

Answers (4)

mjomble
mjomble

Reputation: 541

Alternative solution that doesn't involve number to string conversion:

Math.round(Math.round(n * 1000) / 10) / 100

It's specific to 2 decimals, but you could also generalize it if needed.

In case of negative numbers, it rounds .005 up towards 0, so -0.145 becomes -0.14, which may or may not be what you want.

Let me know if you find a case where this doesn't work correctly.

Upvotes: 0

Yuri Predborski
Yuri Predborski

Reputation: 196

I realize this problem is rather old, but I keep running into it even 5 years after the question has been asked.

A working solution to this rounding problem I know of is to convert the number to a string, get the required precision number and round up or down using math rules.

An example where Math.round provides unexpected rounding and an example of string rounding can be found in the following fiddle: http://jsfiddle.net/Shinigami84/vwx1yjnr/

function round(number, decimals = 0) {
    let strNum = '' + number;
    let negCoef = number < 0 ? -1 : 1;
    let dotIndex = strNum.indexOf('.');
    let start = dotIndex + decimals + 1;
    let dec = Number.parseInt(strNum.substring(start, start + 1));
    let remainder = dec >= 5 ? 1 / Math.pow(10, decimals) : 0;
    let result = Number.parseFloat(strNum.substring(0, start)) + remainder * negCoef;
    return result.toFixed(decimals);
}
let num = 0.145;
let precision = 2;

console.log('math round', Math.round(num*Math.pow(10, precision))/Math.pow(10, precision));
// 0.145 rounded down to 0.14 - unexpected result
console.log('string round', round(num, precision));
// 0.145 rounded up to 0.15 - expected result

Math.round doesn't work properly here because 0.145 multiplied by 100 is 14.499999999999998, not 14.5. Thus, Math.round will round it down as if it was 14.4. If you convert it to a string and subtract required digit (5), then round it using standard math rules, you will get an expected result of 0.15 (actually, 0.14 + 0.01 = 0.15000000000000002, use "toFixed" to get a nice, round result).

Upvotes: 2

Yevgeniy Afanasyev
Yevgeniy Afanasyev

Reputation: 41350

This formula Math.round(num*100)/100 is not always good. Example

Math.round(0.145*100)/100 = 0.14

this is wrong, we want it to be 0.15

Explanation

The problem is that we have floats like that

0.145 * 100 = 14.499999999999998

step one

so If we round, we need to add a little bit to our product.

0.145 * 100 + 1e-14 = 14.500000000000009

I assume that sometimes the product might be something like 1.000000000000001, but it would not be a problem if we add to it, right?

step two

Calculate how much should we add?

We know float in java script is 17 digits.

let num = 0.145
let a = Math.round(num*100)/100
let b = a.toString().length
let c = 17-b-2
let result = Math.round(num*100 + 0.1**c)/100
console.log(result)
console.log('not - ' + a )

(-2) - is just to be sure we are not falling into the same trap of rounding.

One-liner:

let num = 0.145
let result = Math.round(num*100 + 0.1**(17-2-(Math.round(num*100)/100).toString().length))/100

Extras

Remember, that everything above is true for positive numbers. If you rounding negative number you would need to subtract a little bit. So the very final One-liner would be:

let num = -0.145
let result = Math.round(num*100 + Math.sign(num)*0.1**(17-2-(Math.round(num*100)/100).toString().length))/100

Upvotes: 3

John K.
John K.

Reputation: 5474

Try something like...

Math.round(num*100)/100


1) Multiple the original number by 10^x (10 to the power of x)
2) Apply Math.round() to the result
3) Divide result by 10^x

from: http://www.javascriptkit.com/javatutors/round.shtml

(to round any number to x decimal points)

Upvotes: 4

Related Questions