I have this line of code which rounds my numbers to two decimal places. But I get numbers like this: 10.8, 2.4, etc. These are not my idea of two decimal places so how I can improve the following?
Math.round(price*Math.pow(10,2))/Math.pow(10,2);
I want numbers like 10.80, 2.40, etc. Use of jQuery is fine with me.
This question is related to
javascript
rounding
decimal-point
Here's a TypeScript implementation of https://stackoverflow.com/a/21323330/916734. It also dries things up with functions, and allows for a optional digit offset.
export function round(rawValue: number | string, precision = 0, fractionDigitOffset = 0): number | string {
const value = Number(rawValue);
if (isNaN(value)) return rawValue;
precision = Number(precision);
if (precision % 1 !== 0) return NaN;
let [ stringValue, exponent ] = scientificNotationToParts(value);
let shiftExponent = exponentForPrecision(exponent, precision, Shift.Right);
const enlargedValue = toScientificNotation(stringValue, shiftExponent);
const roundedValue = Math.round(enlargedValue);
[ stringValue, exponent ] = scientificNotationToParts(roundedValue);
const precisionWithOffset = precision + fractionDigitOffset;
shiftExponent = exponentForPrecision(exponent, precisionWithOffset, Shift.Left);
return toScientificNotation(stringValue, shiftExponent);
}
enum Shift {
Left = -1,
Right = 1,
}
function scientificNotationToParts(value: number): Array<string> {
const [ stringValue, exponent ] = value.toString().split('e');
return [ stringValue, exponent ];
}
function exponentForPrecision(exponent: string, precision: number, shift: Shift): number {
precision = shift * precision;
return exponent ? (Number(exponent) + precision) : precision;
}
function toScientificNotation(value: string, exponent: number): number {
return Number(`${value}e${exponent}`);
}
I'm fix the problem the modifier. Support 2 decimal only.
$(function(){_x000D_
//input number only._x000D_
convertNumberFloatZero(22); // output : 22.00_x000D_
convertNumberFloatZero(22.5); // output : 22.50_x000D_
convertNumberFloatZero(22.55); // output : 22.55_x000D_
convertNumberFloatZero(22.556); // output : 22.56_x000D_
convertNumberFloatZero(22.555); // output : 22.55_x000D_
convertNumberFloatZero(22.5541); // output : 22.54_x000D_
convertNumberFloatZero(22222.5541); // output : 22,222.54_x000D_
_x000D_
function convertNumberFloatZero(number){_x000D_
if(!$.isNumeric(number)){_x000D_
return 'NaN';_x000D_
}_x000D_
var numberFloat = number.toFixed(3);_x000D_
var splitNumber = numberFloat.split(".");_x000D_
var cNumberFloat = number.toFixed(2);_x000D_
var cNsplitNumber = cNumberFloat.split(".");_x000D_
var lastChar = splitNumber[1].substr(splitNumber[1].length - 1);_x000D_
if(lastChar > 0 && lastChar < 5){_x000D_
cNsplitNumber[1]--;_x000D_
}_x000D_
return Number(splitNumber[0]).toLocaleString('en').concat('.').concat(cNsplitNumber[1]);_x000D_
};_x000D_
});
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
_x000D_
Here's a simple one
function roundFloat(num,dec){
var d = 1;
for (var i=0; i<dec; i++){
d += "0";
}
return Math.round(num * d) / d;
}
Use like alert(roundFloat(1.79209243929,4));
@heridev and I created a small function in jQuery.
You can try next:
HTML
<input type="text" name="one" class="two-digits"><br>
<input type="text" name="two" class="two-digits">?
jQuery
// apply the two-digits behaviour to elements with 'two-digits' as their class
$( function() {
$('.two-digits').keyup(function(){
if($(this).val().indexOf('.')!=-1){
if($(this).val().split(".")[1].length > 2){
if( isNaN( parseFloat( this.value ) ) ) return;
this.value = parseFloat(this.value).toFixed(2);
}
}
return this; //for chaining
});
});
? DEMO ONLINE:
This is very simple and works just as well as any of the others:
function parseNumber(val, decimalPlaces) {
if (decimalPlaces == null) decimalPlaces = 0
var ret = Number(val).toFixed(decimalPlaces)
return Number(ret)
}
Since toFixed() can only be called on numbers, and unfortunately returns a string, this does all the parsing for you in both directions. You can pass a string or a number, and you get a number back every time! Calling parseNumber(1.49) will give you 1, and parseNumber(1.49,2) will give you 1.50. Just like the best of 'em!
Number(((Math.random() * 100) + 1).toFixed(2))
this will return a random number from 1 to 100 rounded to 2 decimal places.
Round down
function round_down(value, decPlaces) {
return Math.floor(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}
Round up
function round_up(value, decPlaces) {
return Math.ceil(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}
Round nearest
function round_nearest(value, decPlaces) {
return Math.round(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}
Merged https://stackoverflow.com/a/7641824/1889449 and https://www.kirupa.com/html5/rounding_numbers_in_javascript.htm Thanks them.
I didn't find an accurate solution for this problem, so I created my own:
function inprecise_round(value, decPlaces) {
return Math.round(value*Math.pow(10,decPlaces))/Math.pow(10,decPlaces);
}
function precise_round(value, decPlaces){
var val = value * Math.pow(10, decPlaces);
var fraction = (Math.round((val-parseInt(val))*10)/10);
//this line is for consistency with .NET Decimal.Round behavior
// -342.055 => -342.06
if(fraction == -0.5) fraction = -0.6;
val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces);
return val;
}
Examples:
function inprecise_round(value, decPlaces) {_x000D_
return Math.round(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);_x000D_
}_x000D_
_x000D_
function precise_round(value, decPlaces) {_x000D_
var val = value * Math.pow(10, decPlaces);_x000D_
var fraction = (Math.round((val - parseInt(val)) * 10) / 10);_x000D_
_x000D_
//this line is for consistency with .NET Decimal.Round behavior_x000D_
// -342.055 => -342.06_x000D_
if (fraction == -0.5) fraction = -0.6;_x000D_
_x000D_
val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces);_x000D_
return val;_x000D_
}_x000D_
_x000D_
// This may produce different results depending on the browser environment_x000D_
console.log("342.055.toFixed(2) :", 342.055.toFixed(2)); // 342.06 on Chrome & IE10_x000D_
_x000D_
console.log("inprecise_round(342.055, 2):", inprecise_round(342.055, 2)); // 342.05_x000D_
console.log("precise_round(342.055, 2) :", precise_round(342.055, 2)); // 342.06_x000D_
console.log("precise_round(-342.055, 2) :", precise_round(-342.055, 2)); // -342.06_x000D_
_x000D_
console.log("inprecise_round(0.565, 2) :", inprecise_round(0.565, 2)); // 0.56_x000D_
console.log("precise_round(0.565, 2) :", precise_round(0.565, 2)); // 0.57
_x000D_
(Math.round((10.2)*100)/100).toFixed(2)
That should yield: 10.20
(Math.round((.05)*100)/100).toFixed(2)
That should yield: 0.05
(Math.round((4.04)*100)/100).toFixed(2)
That should yield: 4.04
etc.
toFixed(n) provides n length after the decimal point; toPrecision(x) provides x total length.
Use this method below
// Example: toPrecision(4) when the number has 7 digits (3 before, 4 after)
// It will round to the tenths place
num = 500.2349;
result = num.toPrecision(4); // result will equal 500.2
AND if you want the number to be fixed use
result = num.toFixed(2);
Here is my 1-line solution: Number((yourNumericValueHere).toFixed(2));
Here's what happens:
1) First, you apply .toFixed(2)
onto the number that you want to round off the decimal places of. Note that this will convert the value to a string from number. So if you are using Typescript, it will throw an error like this:
"Type 'string' is not assignable to type 'number'"
2) To get back the numeric value or to convert the string to numeric value, simply apply the Number()
function on that so-called 'string' value.
For clarification, look at the example below:
EXAMPLE: I have an amount that has upto 5 digits in the decimal places and I would like to shorten it to upto 2 decimal places. I do it like so:
var price = 0.26453;_x000D_
var priceRounded = Number((price).toFixed(2));_x000D_
console.log('Original Price: ' + price);_x000D_
console.log('Price Rounded: ' + priceRounded);
_x000D_
With these examples you will still get an error when trying to round the number 1.005 the solution is to either use a library like Math.js or this function:
function round(value: number, decimals: number) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
Using this response by reference: https://stackoverflow.com/a/21029698/454827
I build a function to get dynamic numbers of decimals:
function toDec(num, dec)
{
if(typeof dec=='undefined' || dec<0)
dec = 2;
var tmp = dec + 1;
for(var i=1; i<=tmp; i++)
num = num * 10;
num = num / 10;
num = Math.round(num);
for(var i=1; i<=dec; i++)
num = num / 10;
num = num.toFixed(dec);
return num;
}
here working example: https://jsfiddle.net/wpxLduLc/
Round your decimal value, then use toFixed(x)
for your expected digit(s).
function parseDecimalRoundAndFixed(num,dec){
var d = Math.pow(10,dec);
return (Math.round(num * d) / d).toFixed(dec);
}
Call
parseDecimalRoundAndFixed(10.800243929,4) => 10.80 parseDecimalRoundAndFixed(10.807243929,2) => 10.81
parse = function (data) {_x000D_
data = Math.round(data*Math.pow(10,2))/Math.pow(10,2);_x000D_
if (data != null) {_x000D_
var lastone = data.toString().split('').pop();_x000D_
if (lastone != '.') {_x000D_
data = parseFloat(data);_x000D_
}_x000D_
}_x000D_
return data;_x000D_
};_x000D_
_x000D_
$('#result').html(parse(200)); // output 200_x000D_
$('#result1').html(parse(200.1)); // output 200.1_x000D_
$('#result2').html(parse(200.10)); // output 200.1_x000D_
$('#result3').html(parse(200.109)); // output 200.11
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>_x000D_
<div id="result"></div>_x000D_
<div id="result1"></div>_x000D_
<div id="result2"></div>_x000D_
<div id="result3"></div>
_x000D_
/*Due to all told stuff. You may do 2 things for different purposes:_x000D_
When showing/printing stuff use this in your alert/innerHtml= contents:_x000D_
YourRebelNumber.toFixed(2)*/_x000D_
_x000D_
var aNumber=9242.16;_x000D_
var YourRebelNumber=aNumber-9000;_x000D_
alert(YourRebelNumber);_x000D_
alert(YourRebelNumber.toFixed(2));_x000D_
_x000D_
/*and when comparing use:_x000D_
Number(YourRebelNumber.toFixed(2))*/_x000D_
_x000D_
if(YourRebelNumber==242.16)alert("Not Rounded");_x000D_
if(Number(YourRebelNumber.toFixed(2))==242.16)alert("Rounded");_x000D_
_x000D_
/*Number will behave as you want in that moment. After that, it'll return to its defiance._x000D_
*/
_x000D_
I found a very simple way that solved this problem for me and can be used or adapted:
td[row].innerHTML = price.toPrecision(price.toFixed(decimals).length
100% working!!! Try it
<html>_x000D_
<head>_x000D_
<script>_x000D_
function replacePonto(){_x000D_
var input = document.getElementById('qtd');_x000D_
var ponto = input.value.split('.').length;_x000D_
var slash = input.value.split('-').length;_x000D_
if (ponto > 2)_x000D_
input.value=input.value.substr(0,(input.value.length)-1);_x000D_
_x000D_
if(slash > 2)_x000D_
input.value=input.value.substr(0,(input.value.length)-1);_x000D_
_x000D_
input.value=input.value.replace(/[^0-9.-]/,'');_x000D_
_x000D_
if (ponto ==2)_x000D_
input.value=input.value.substr(0,(input.value.indexOf('.')+3));_x000D_
_x000D_
if(input.value == '.')_x000D_
input.value = "";_x000D_
}_x000D_
</script>_x000D_
</head>_x000D_
<body>_x000D_
<input type="text" id="qtd" maxlength="10" style="width:140px" onkeyup="return replacePonto()">_x000D_
</body>_x000D_
</html>
_x000D_
This is an old topic but still top-ranked Google results and the solutions offered share the same floating point decimals issue. Here is the (very generic) function I use, thanks to MDN:
function round(value, exp) {
if (typeof exp === 'undefined' || +exp === 0)
return Math.round(value);
value = +value;
exp = +exp;
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
return NaN;
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}
As we can see, we don't get these issues:
round(1.275, 2); // Returns 1.28
round(1.27499, 2); // Returns 1.27
This genericity also provides some cool stuff:
round(1234.5678, -2); // Returns 1200
round(1.2345678e+2, 2); // Returns 123.46
round("123.45"); // Returns 123
Now, to answer the OP's question, one has to type:
round(10.8034, 2).toFixed(2); // Returns "10.80"
round(10.8, 2).toFixed(2); // Returns "10.80"
Or, for a more concise, less generic function:
function round2Fixed(value) {
value = +value;
if (isNaN(value))
return NaN;
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + 2) : 2)));
// Shift back
value = value.toString().split('e');
return (+(value[0] + 'e' + (value[1] ? (+value[1] - 2) : -2))).toFixed(2);
}
You can call it with:
round2Fixed(10.8034); // Returns "10.80"
round2Fixed(10.8); // Returns "10.80"
Various examples and tests (thanks to @t-j-crowder!):
function round(value, exp) {_x000D_
if (typeof exp === 'undefined' || +exp === 0)_x000D_
return Math.round(value);_x000D_
_x000D_
value = +value;_x000D_
exp = +exp;_x000D_
_x000D_
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))_x000D_
return NaN;_x000D_
_x000D_
// Shift_x000D_
value = value.toString().split('e');_x000D_
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));_x000D_
_x000D_
// Shift back_x000D_
value = value.toString().split('e');_x000D_
return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));_x000D_
}_x000D_
function naive(value, exp) {_x000D_
if (!exp) {_x000D_
return Math.round(value);_x000D_
}_x000D_
var pow = Math.pow(10, exp);_x000D_
return Math.round(value * pow) / pow;_x000D_
}_x000D_
function test(val, places) {_x000D_
subtest(val, places);_x000D_
val = typeof val === "string" ? "-" + val : -val;_x000D_
subtest(val, places);_x000D_
}_x000D_
function subtest(val, places) {_x000D_
var placesOrZero = places || 0;_x000D_
var naiveResult = naive(val, places);_x000D_
var roundResult = round(val, places);_x000D_
if (placesOrZero >= 0) {_x000D_
naiveResult = naiveResult.toFixed(placesOrZero);_x000D_
roundResult = roundResult.toFixed(placesOrZero);_x000D_
} else {_x000D_
naiveResult = naiveResult.toString();_x000D_
roundResult = roundResult.toString();_x000D_
}_x000D_
$("<tr>")_x000D_
.append($("<td>").text(JSON.stringify(val)))_x000D_
.append($("<td>").text(placesOrZero))_x000D_
.append($("<td>").text(naiveResult))_x000D_
.append($("<td>").text(roundResult))_x000D_
.appendTo("#results");_x000D_
}_x000D_
test(0.565, 2);_x000D_
test(0.575, 2);_x000D_
test(0.585, 2);_x000D_
test(1.275, 2);_x000D_
test(1.27499, 2);_x000D_
test(1234.5678, -2);_x000D_
test(1.2345678e+2, 2);_x000D_
test("123.45");_x000D_
test(10.8034, 2);_x000D_
test(10.8, 2);_x000D_
test(1.005, 2);_x000D_
test(1.0005, 2);
_x000D_
table {_x000D_
border-collapse: collapse;_x000D_
}_x000D_
table, td, th {_x000D_
border: 1px solid #ddd;_x000D_
}_x000D_
td, th {_x000D_
padding: 4px;_x000D_
}_x000D_
th {_x000D_
font-weight: normal;_x000D_
font-family: sans-serif;_x000D_
}_x000D_
td {_x000D_
font-family: monospace;_x000D_
}
_x000D_
<table>_x000D_
<thead>_x000D_
<tr>_x000D_
<th>Input</th>_x000D_
<th>Places</th>_x000D_
<th>Naive</th>_x000D_
<th>Thorough</th>_x000D_
</tr>_x000D_
</thead>_x000D_
<tbody id="results">_x000D_
</tbody>_x000D_
</table>_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
_x000D_
You could also use the .toPrecision()
method and some custom code, and always round up to the nth decimal digit regardless the length of int part.
function glbfrmt (number, decimals, seperator) {
return typeof number !== 'number' ? number : number.toPrecision( number.toString().split(seperator)[0].length + decimals);
}
You could also make it a plugin for a better use.
I got some ideas from this post a few months back, but none of the answers here, nor answers from other posts/blogs could handle all the scenarios (e.g. negative numbers and some "lucky numbers" our tester found). In the end, our tester did not find any problem with this method below. Pasting a snippet of my code:
fixPrecision: function (value) {
var me = this,
nan = isNaN(value),
precision = me.decimalPrecision;
if (nan || !value) {
return nan ? '' : value;
} else if (!me.allowDecimals || precision <= 0) {
precision = 0;
}
//[1]
//return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
precision = precision || 0;
var negMultiplier = value < 0 ? -1 : 1;
//[2]
var numWithExp = parseFloat(value + "e" + precision);
var roundedNum = parseFloat(Math.round(Math.abs(numWithExp)) + 'e-' + precision) * negMultiplier;
return parseFloat(roundedNum.toFixed(precision));
},
I also have code comments (sorry i forgot all the details already)...I'm posting my answer here for future reference:
9.995 * 100 = 999.4999999999999
Whereas 9.995e2 = 999.5
This discrepancy causes Math.round(9.995 * 100) = 999 instead of 1000.
Use e notation instead of multiplying /dividing by Math.Pow(10,precision).
Put the following in some global scope:
Number.prototype.getDecimals = function ( decDigCount ) {
return this.toFixed(decDigCount);
}
and then try:
var a = 56.23232323;
a.getDecimals(2); // will return 56.23
Note that toFixed()
can only work for the number of decimals between 0-20
i.e. a.getDecimals(25)
may generate a javascript error, so to accomodate that you may add some additional check i.e.
Number.prototype.getDecimals = function ( decDigCount ) {
return ( decDigCount > 20 ) ? this : this.toFixed(decDigCount);
}
One way to be 100% sure that you get a number with 2 decimals:
(Math.round(num*100)/100).toFixed(2)
If this causes rounding errors, you can use the following as James has explained in his comment:
(Math.round((num * 1000)/10)/100).toFixed(2)
I usually add this to my personal library, and after some suggestions and using the @TIMINeutron solution too, and making it adaptable for decimal length then, this one fits best:
function precise_round(num, decimals) {
var t = Math.pow(10, decimals);
return (Math.round((num * t) + (decimals>0?1:0)*(Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals);
}
will work for the exceptions reported.
parseFloat(number.toFixed(2))
let number = 2.55435930
let roundedString = number.toFixed(2) // "2.55"
let twoDecimalsNumber = parseFloat(roundedString) // 2.55
let directly = parseFloat(number.toFixed(2)) // 2.55
Number(Math.round(1.005+'e2')+'e-2'); // 1.01
This worked for me: Rounding Decimals in JavaScript
The trouble with floating point values is that they are trying to represent an infinite amount of (continuous) values with a fixed amount of bits. So naturally, there must be some loss in play, and you're going to be bitten with some values.
When a computer stores 1.275 as a floating point value, it won't actually remember whether it was 1.275 or 1.27499999999999993, or even 1.27500000000000002. These values should give different results after rounding to two decimals, but they won't, since for computer they look exactly the same after storing as floating point values, and there's no way to restore the lost data. Any further calculations will only accumulate such imprecision.
So, if precision matters, you have to avoid floating point values from the start. The simplest options are to
For example, when using integers to store the number of hundredths, the function for finding the actual value is quite simple:
function descale(num, decimals) {
var hasMinus = num < 0;
var numString = Math.abs(num).toString();
var precedingZeroes = '';
for (var i = numString.length; i <= decimals; i++) {
precedingZeroes += '0';
}
numString = precedingZeroes + numString;
return (hasMinus ? '-' : '')
+ numString.substr(0, numString.length-decimals)
+ '.'
+ numString.substr(numString.length-decimals);
}
alert(descale(127, 2));
With strings, you'll need rounding, but it's still manageable:
function precise_round(num, decimals) {
var parts = num.split('.');
var hasMinus = parts.length > 0 && parts[0].length > 0 && parts[0].charAt(0) == '-';
var integralPart = parts.length == 0 ? '0' : (hasMinus ? parts[0].substr(1) : parts[0]);
var decimalPart = parts.length > 1 ? parts[1] : '';
if (decimalPart.length > decimals) {
var roundOffNumber = decimalPart.charAt(decimals);
decimalPart = decimalPart.substr(0, decimals);
if ('56789'.indexOf(roundOffNumber) > -1) {
var numbers = integralPart + decimalPart;
var i = numbers.length;
var trailingZeroes = '';
var justOneAndTrailingZeroes = true;
do {
i--;
var roundedNumber = '1234567890'.charAt(parseInt(numbers.charAt(i)));
if (roundedNumber === '0') {
trailingZeroes += '0';
} else {
numbers = numbers.substr(0, i) + roundedNumber + trailingZeroes;
justOneAndTrailingZeroes = false;
break;
}
} while (i > 0);
if (justOneAndTrailingZeroes) {
numbers = '1' + trailingZeroes;
}
integralPart = numbers.substr(0, numbers.length - decimals);
decimalPart = numbers.substr(numbers.length - decimals);
}
} else {
for (var i = decimalPart.length; i < decimals; i++) {
decimalPart += '0';
}
}
return (hasMinus ? '-' : '') + integralPart + (decimals > 0 ? '.' + decimalPart : '');
}
alert(precise_round('1.275', 2));
alert(precise_round('1.27499999999999993', 2));
Note that this function rounds to nearest, ties away from zero, while IEEE 754 recommends rounding to nearest, ties to even as the default behavior for floating point operations. Such modifications are left as an exercise for the reader :)
In general, decimal rounding is done by scaling: round(num * p) / p
Naive implementation
Using the following function with halfway numbers, you will get either the upper rounded value as expected, or the lower rounded value sometimes depending on the input.
This inconsistency
in rounding may introduce hard to detect bugs in the client code.
function naiveRound(num, decimalPlaces) {
var p = Math.pow(10, decimalPlaces);
return Math.round(num * p) / p;
}
console.log( naiveRound(1.245, 2) ); // 1.25 correct (rounded as expected)
console.log( naiveRound(1.255, 2) ); // 1.25 incorrect (should be 1.26)
_x000D_
Better implementations
By converting the number to a string in the exponential notation, positive numbers are rounded as expected. But, be aware that negative numbers round differently than positive numbers.
In fact, it performs what is basically equivalent to "round half up" as the rule, you will see that round(-1.005, 2)
evaluates to -1
even though round(1.005, 2)
evaluates to 1.01
. The lodash _.round method uses this technique.
/**
* Round half up ('round half towards positive infinity')
* Uses exponential notation to avoid floating-point issues.
* Negative numbers round differently than positive numbers.
*/
function round(num, decimalPlaces) {
num = Math.round(num + "e" + decimalPlaces);
return Number(num + "e" + -decimalPlaces);
}
// test rounding of half
console.log( round(0.5, 0) ); // 1
console.log( round(-0.5, 0) ); // 0
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1
console.log( round(-2.175, 2) ); // -2.17
console.log( round(-5.015, 2) ); // -5.01
_x000D_
If you want the usual behavior when rounding negative numbers, you would need to convert negative numbers to positive before calling Math.round(), and then convert them back to negative numbers before returning.
// Round half away from zero
function round(num, decimalPlaces) {
num = Math.round(Math.abs(num) + "e" + decimalPlaces) * Math.sign(num);
return Number(num + "e" + -decimalPlaces);
}
There is a different purely mathematical technique to perform round-to-nearest (using "round half away from zero"), in which epsilon correction is applied before calling the rounding function.
Simply, we add the smallest possible float value (= 1.0 ulp; unit in the last place) to the number before rounding. This moves to the next representable value after the number, away from zero.
/**
* Round half away from zero ('commercial' rounding)
* Uses correction to offset floating-point inaccuracies.
* Works symmetrically for positive and negative numbers.
*/
function round(num, decimalPlaces) {
var p = Math.pow(10, decimalPlaces);
var e = Number.EPSILON * num * p;
return Math.round((num * p) + e) / p;
}
// test rounding of half
console.log( round(0.5, 0) ); // 1
console.log( round(-0.5, 0) ); // -1
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
_x000D_
This is needed to offset the implicit round-off error that may occur during encoding of decimal numbers, particularly those having "5" in the last decimal position, like 1.005, 2.675 and 16.235. Actually, 1.005
in decimal system is encoded to 1.0049999999999999
in 64-bit binary float; while, 1234567.005
in decimal system is encoded to 1234567.0049999998882413
in 64-bit binary float.
It is worth noting that the maximum binary round-off error
is dependent upon (1) the magnitude of the number and (2) the relative machine epsilon (2^-52).
I don't know why can't I add a comment to a previous answer (maybe I'm hopelessly blind, I don't know), but I came up with a solution using @Miguel's answer:
function precise_round(num,decimals) {
return Math.round(num*Math.pow(10, decimals)) / Math.pow(10, decimals);
}
And its two comments (from @bighostkim and @Imre):
precise_round(1.275,2)
not returning 1.28precise_round(6,2)
not returning 6.00 (as he wanted).My final solution is as follows:
function precise_round(num,decimals) {
var sign = num >= 0 ? 1 : -1;
return (Math.round((num*Math.pow(10,decimals)) + (sign*0.001)) / Math.pow(10,decimals)).toFixed(decimals);
}
As you can see I had to add a little bit of "correction" (it's not what it is, but since Math.round is lossy - you can check it on jsfiddle.net - this is the only way I knew how to "fix" it). It adds 0.001 to the already padded number, so it is adding a 1
three 0
s to the right of the decimal value. So it should be safe to use.
After that I added .toFixed(decimal)
to always output the number in the correct format (with the right amount of decimals).
So that's pretty much it. Use it well ;)
EDIT: added functionality to the "correction" of negative numbers.
Source: Stackoverflow.com