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 < 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 < precision + 1) {
for (var i = 0; i < (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 >= 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 < 0 ? -1 : 1);
}
number = +number.toFixed(precision);
return number;
};

roundfunction as well. \$\endgroup\$(e.g. round(1254, -2)) => 1300 )so I mentioned that as well. \$\endgroup\$