Rounding of negative numbers in Javascript

2020-06-13 05:28发布

问题:

We have come across a problem with Math.round() in JavaScript. The problem is that this function doesn't round correctly for negative numbers. For example :

1.5 ~= 2

0.5 ~= 1

-0.5 ~= 0 // Wrong

-1.5 ~= -1 // Wrong

And this is not correct according to arithmetic rounding. The correct numbers for -0.5 should be -1 and -1.5 should be -2.

Is there any standard way, to correctly round negative numbers in Javascript ?

回答1:

You could save the sign and apply later, in ES5;

function round(v) {
    return (v >= 0 || -1) * Math.round(Math.abs(v));
}

console.log(round(1.5));  //  2
console.log(round(0.5));  //  1
console.log(round(-1.5)); // -2
console.log(round(-0.5)); // -1



回答2:

Apply Math.round after converting to a positive number and finally roll back the sign. Where you can use Math.sign method to get the sign from the number and Math.abs to get the absolute of the number.

console.log(
  Math.sign(num) * Math.round(Math.sign(num) * num),
  // or
  Math.sign(num) * Math.round(Math.abs(num))
)

var nums = [-0.5, 1.5, 3, 3.6, -4.8, -1.3];

nums.forEach(function(num) {
  console.log(
    Math.sign(num) * Math.round(Math.sign(num) * num),
    Math.sign(num) * Math.round(Math.abs(num))
  )
});



回答3:

You could try using Math.ceil(num) to round num and then - 1 if num was negative, e.g.

if (num < 0) {
  num = Math.ceil(num) - 1;
} else {
  num = Math.ceil(num);
}


回答4:

var r = (Math.random() * 200) - 100;

How about purely evaluating the result without using the math library? eg, by using a ternary operator, you can elegantly check for negative numbers and then floor after "rounding":

var n = r + (r < 0 ? -0.5 : 0.5) | 0;

The | 0 is just a silly trick in js that "overloads" the binary operator (you could use any binary operator) in order to truncate the number.

Note that this is not flooring (like Math.floor), since Math.floor(-3.2), for example, will actually yield -4.


One could even do something similar to @Balan's answer (I like that one and the one below, but I feel like this or the ternary operator will just be a touch faster--I am probably wrong, though, because the Math libraries have been proven to be very fast):

var n = (r + Math.sign(r) / 2) | 0;

probably the fastest, most elegant way:

var n = Math.floor(r + 0.5);

example:

var body = document.getElementById("myTable").children[1];
var i, iMax = 100, r, tr, td;
for (i = 0; i < iMax; i++) {
    r = Math.random() * 200 - 100;
    tr = document.createElement("tr");
    td = document.createElement("td");
    td.innerHTML = r;
    tr.appendChild(td);
    td = document.createElement("td");
    td.innerHTML = (r + Math.sign(r) / 2) | 0;
    tr.appendChild(td);
    body.appendChild(tr);
}
#myTable {
    min-width: 250px;
}
<table id="myTable">
 <thead>
  <tr>
   <th>float</th>
   <th>round</th>
  </tr>
 </thead>
 <tbody>
 </tbody>
</table>



回答5:

Rounding with precision.
Rounds to Int by default or, if second argument is provided, to n digits after decimal point.
+ '1' fixes incorrect rounding for .0X5 numbers.

function round(num, digits=0) {
    if (!(num % 1)) return num
    return +Number(num + '1').toFixed(digits)
}