Rounding JavaScript decimals JavaScript has some inaccurate rounding behavior so I've been looking for a reliable solution without success. There are many answers in this SO post but none cover all the edge cases as far as I can tell. I wrote the following which handles all the edge cases presented. Will it be reliable with edge cases I haven't tested?

If this is a viable solution, any enhancements to make it more efficient would be appreciated. It's not fast (time to run function 1000000 times: 778ms) but doesn't seem to be  terrible either. If there is a better solution, please post.

The edge cases that seemed to give the most problem were the first two:



console.log(round(1.005, 2)); // 1.01
console.log(round(1234.00000254495, 10)); //1234.000002545
console.log(round(1835.665, 2)); // 1835.67))
console.log(round(-1835.665, 2)); // -1835.67))
console.log(round(10.8034, 2)); // 10.8
console.log(round(1.275, 2)); // 1.28
console.log(round(1.27499, 2)); // 1.27
console.log(round(1.2345678e+2, 2)); // 123.46
console.log(round(1234.5678, -1)); // 1230
console.log(round(1235.5678, -1)); // 1240
console.log(round(1234.5678, -2)); // 1200
console.log(round(1254.5678, -2)); // 1300
console.log(round(1254, 2)); // 1254
console.log(round("123.45")); // 123
console.log(round("123.55")); // 124

function round(number, precision) {
  precision = precision ? precision : 0;

  var sNumber = "" + number;
  var a = sNumber.split(".");
  if (a.length == 1 || precision &lt; 0) {
    // from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
  }
  // use one decimal place beyond the precision
  var factor = Math.pow(10, precision + 1);
  // separate out decimals and trim or pad as necessary
  var sDec = a[1].substr(0, precision + 1);
  if (sDec.length &lt; precision + 1) {
    for (var i = 0; i &lt; (precision - sDec.length); i++)
      sDec = sDec.concat("0");
  }
  // put the number back together
  var sNumber = a[0] + "." + sDec;
  var number = parseFloat(sNumber);
  // test the last digit
  var last = sDec.substr(sDec.length - 1);
  if (last &gt;= 5) {
    // round up by correcting float error
    // UPDATED - for negative numbers will round away from 0 
    // e.g. round(-2.5, 0) == -3        
    number += 1/(factor) * (+number &lt; 0 ? -1 : 1);
  }
  number = +number.toFixed(precision);
  return number;
};
Here is a solution which is quick and handles all cases - negative number and negative precision. It was adapted from MDN - the main differences are


I pass positive numbers to specify decimal places where it is the opposite on MDN.
I took it out of the prototype and limited it to just the round function (removed ceil and floor) 
it is significantly faster




function round(number, precision) {
  number = +number;
  precision = precision ? +precision : 0;
  if (precision == 0) {
    return Math.round(number);
  }
  var sign = 1;
  if (number &lt; 0) {
    sign = -1;
    number = Math.abs(number);
  }

  // Shift
  number = number.toString().split('e');
  number = Math.round(+(number[0] + 'e' + (number[1] ? (+number[1] + precision) : precision)));
  // Shift back
  number = number.toString().split('e');
  return +(number[0] + 'e' + (number[1] ? (+number[1] - precision) : -precision)) * sign;
}

console.log(round(1.0e-5, 5)); // 0.00001
console.log(round(1.0e-20, 20)); // 1e-20
console.log(round(1.0e20, 2)); // 100000000000000000000
console.log(round(1.005, 2)); // 1.01
console.log(round(1234.00000254495, 10)); //1234.000002545
console.log(round(1835.665, 2)); // 1835.67))
console.log(round(-1835.665, 2)); // -1835.67))
console.log(round(1.27499, 2)); // 1.27
console.log(round(1.2345678e+2, 2)); // 123.46
console.log(round(1234.5678, -1)); // 1230
console.log(round(1234.5678, -2)); // 1200
console.log(round(1254.5678, -2)); // 1300
console.log(round(1254, 2)); // 1254
console.log(round("123.55")); // 124