136

in JavaScript, the typical way to round a number to N decimal places is something like:

function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

console.log(roundNumber(0.1 + 0.2, 2));
console.log(roundNumber(2.1234, 2));

However this approach will round to a maximum of N decimal places while I want to always round to N decimal places. For example "2.0" would be rounded to "2".

Any ideas?

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
hoju
  • 28,392
  • 37
  • 134
  • 178
  • 10
    normally, you could use `toFixed()` ( https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Number/ToFixed ), but it's buggy in IE: http://stackoverflow.com/questions/661562/how-to-format-a-float-in-javascript/661757#661757 ; you'll have to write your own version... – Christoph Feb 08 '10 at 11:26
  • 1
    @hoju - perhaps change accepted answer - David's answer is correct for IE8+, while the accepted answer has some serious bugs on all browsers. – robocat May 26 '15 at 23:02
  • @robocat: Are you serious? – Guffa May 27 '15 at 00:51

11 Answers11

288

I think that there is a more simple approach to all given here, and is the method Number.toFixed() already implemented in JavaScript.

simply write:

var myNumber = 2;

myNumber.toFixed(2); //returns "2.00"
myNumber.toFixed(1); //returns "2.0"

etc...

P Varga
  • 19,174
  • 12
  • 70
  • 108
David
  • 4,336
  • 2
  • 23
  • 31
  • Where is mentioned, hoju? I reviewed other answers and I didn't find anyone reporting that toFixed function is buggy. Thanks – David Apr 21 '14 at 07:40
  • @David: That's mentioned in a comment to the answer, for example. – Guffa Jun 05 '15 at 15:55
  • 33
    caution: `toFixed()` returns a string. – Jordan Arsenault Dec 01 '15 at 22:33
  • 2
    @JordanArseno this can be fixed using parseInt(myNumber.toFixed(intVar)); to return an integer value, or parseFloat(myNumber.toFixed(floatVar)); to return a float if user has decimal places. – Stephen Romero Aug 03 '18 at 20:24
  • I don't agree this being a correct answer. It returns a string, not a number. So it doesn't round a number to N decimals, it creates a string that humans can read as a number with decimals. But ony if they, by accident, use the same sign for decimals. use parseFloat(myNumber.toFixed(2)); to get a float of your number (And hope for the best, rounding issues might occur) – Joeri Jun 04 '23 at 11:25
52

I found a way. This is Christoph's code with a fix:

function toFixed(value, precision) {
    var precision = precision || 0,
        power = Math.pow(10, precision),
        absValue = Math.abs(Math.round(value * power)),
        result = (value < 0 ? '-' : '') + String(Math.floor(absValue / power));

    if (precision > 0) {
        var fraction = String(absValue % power),
            padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
        result += '.' + padding + fraction;
    }
    return result;
}

Read the details of repeating a character using an array constructor here if you are curious as to why I added the "+ 1".

Community
  • 1
  • 1
Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
  • Passing in a value of 708.3333333333333 with a precision of 2 or 3 results in a return value of 708.00 for me. I need this for 2 decimal places, in Chrome and IE 9 .toFixed(2) met my needs. – alan Sep 20 '13 at 14:48
  • this is not a common way, e.g, toFixed(16.775, 2) return 16.77. Convert number to String then convert is the only way. – hiway Jun 04 '14 at 03:49
  • 2
    There is a bug with this method: toFixed(-0.1111, 2) returns 0.11, i.e. the negative sign is lost. – Matt Oct 20 '14 at 02:40
  • Works great, except for that I have added parseFloat(result) at the end. Worth an edit. – Artur Barseghyan Mar 25 '15 at 17:01
26

That's not a rounding ploblem, that is a display problem. A number doesn't contain information about significant digits; the value 2 is the same as 2.0000000000000. It's when you turn the rounded value into a string that you have make it display a certain number of digits.

You could just add zeroes after the number, something like:

var s = number.toString();
if (s.indexOf('.') == -1) s += '.';
while (s.length < s.indexOf('.') + 4) s += '0';

(Note that this assumes that the regional settings of the client uses period as decimal separator, the code needs some more work to function for other settings.)

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 3
    toFixed is buggy..for instance the decimal: 1.02449999998 if u do dec.toFixed(4) => 1.0244. It should have been 1.0245 instead. – Bat_Programmer Sep 19 '11 at 01:58
  • 2
    @deepeshk But what would be the problem with using `toFixed()` to pad decimals at the end, after rounding? – pauloya Dec 15 '11 at 17:11
  • 11
    @deepeshk in what browser? Just tried it in Chrome 17 and `1.02449999998.toFixed(4)` correctly returns `1.0245`. – Matt Ball Mar 13 '12 at 14:40
  • @Mark got most of the answer, but after some research you need to call it like this, `Number(float).toFxied(places);`. – Mark Tomlin May 21 '13 at 00:28
  • 3
    @MarkTomlin: Then it seems that you have a string instead of a number. – Guffa May 21 '13 at 01:14
  • @Guffa thanks' for the clarification, I'm kinda fumbling my way through JavaScript programming right now. +1. – Mark Tomlin May 21 '13 at 03:32
  • As an FYI, if you only need precision to 2 decimal places .toFixed works for both IE and Chrome. – alan Sep 20 '13 at 14:50
  • 1
    .toFixed() works brilliantly in modern browsers (I tested Chrome, Firefox, IE11, IE8). @Bat_Programmer - please clarify *which* browsers you think have that bug. – robocat May 07 '15 at 03:57
  • 1
    @robocat: http://stackoverflow.com/questions/661562/how-to-format-a-float-in-javascript/661757#661757 – Guffa May 07 '15 at 07:58
  • 1
    `.toFixed()` is fixed in IE8+ (my testing) but apparently buggy in IE7 (as per @Guffa link in comment above - thank you). So **use** toFixed() unless you need to support IE7 ;-) – robocat May 26 '15 at 22:45
  • 1
    Do NOT USE this answer because it has serious bugs: it gives incorrect answers: NaN -> NaN.000, Infinity -> Infinity.000, 9e60 -> 9E60.000, 0.000001 -> 0.000001 (0.000001.toFixed(3) correctly gives 0.000). – robocat May 26 '15 at 23:01
  • 1
    @robocat: Are you serious? – Guffa May 27 '15 at 00:50
  • 1
    @Guffa - `.toFixed()` works *correctly* in IE8+. e.g. I just run IE8 using the example from the link of `(0.595).toFixed(2)` and got correct answer of `0.60`. Your code is broken for corner cases (the few I thought of from the top of my head with a few minutes testing, I could probably find other things wrong with it), and is not suitable for production code IMHO. – robocat May 28 '15 at 03:40
  • 1
    @robocat: You are looking for something that I never claimed that the code would be. Besdies, you can just as easily find corner cases where `toFixed` is broken, for example `(1000000000000000000000).toFixed(3)` returns `1e+21` not `1000000000000000000000.000`. – Guffa May 28 '15 at 08:29
  • @MattBall no it doesn't, it returns `"1.0245"` – Joeri Jun 04 '23 at 11:30
  • sorry lovely peopls please stop saying toFixed returns a number. `5 + (3).toFixed(2)` returns `"53.00"` – Joeri Jun 04 '23 at 11:32
  • @Guffa incorrect. it returns `"1e+21"` . And your comment doesn't show you know what `Number.MAX_SAFE_INTEGER` is. – Joeri Jun 04 '23 at 11:39
20

There's always a better way for doing things. Use toPrecision -

var number = 51.93999999999761;

I would like to get four digits precision: 51.94

just do:

number.toPrecision(4);

the result will be: 51.94

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
de.la.ru
  • 2,994
  • 1
  • 27
  • 32
  • 4
    And what if you add 100? Do you need to change it to number.toPrecision(5)? – JohnnyBizzle Aug 16 '16 at 08:48
  • 2
    Yes. 31.939383.toPrecision(4) > "31.94" / 131.939383.toPrecision(4) > "131.9" – ReSpawN Jan 03 '17 at 08:51
  • for negative numbers, it changes by 1 decimal. Is there some solution to it? – Geographos Dec 02 '22 at 15:53
  • Note that this doesn't round to n decimals, it rounds to n *sigfigs* and it returns a string. for example, `(1234.56789).toPrecision(4)` returns `1235` NOT `1234.5679` – reticivis Mar 30 '23 at 18:07
  • Sorry peops, this is not the correct anser, the question is about decimals. number.toPrecision(4); does not always give 4 decimals. – Joeri Jun 04 '23 at 11:22
6

This works for rounding to N digits (if you just want to truncate to N digits remove the Math.round call and use the Math.trunc one):

function roundN(value, digits) {
   var tenToN = 10 ** digits;
   return /*Math.trunc*/(Math.round(value * tenToN)) / tenToN;
}

Had to resort to such logic at Java in the past when I was authoring data manipulation E-Slate components. That is since I had found out that adding 0.1 many times to 0 you'd end up with some unexpectedly long decimal part (this is due to floating point arithmetics).

A user comment at Format number to always show 2 decimal places calls this technique scaling.

Some mention there are cases that don't round as expected and at http://www.jacklmoore.com/notes/rounding-in-javascript/ this is suggested instead:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
George Birbilis
  • 2,782
  • 2
  • 33
  • 35
  • btw, POSITS are promising for future h/w architectures: https://www.nextplatform.com/2019/07/08/new-approach-could-sink-floating-point-computation/ – George Birbilis Aug 08 '19 at 13:12
6
function roundton(num, n) {
    return Number(num.toFixed(n));
}

This uses JS's built-in method Number.prototype.toFixed which is meant for formatting strings but allows us to round to a specific number of digits. the Number() call converts it back to a number object cleanly

Ideally, we wouldn't need to convert it to a string, but toFixed is written in native C++ doing basic cstring operations so it's likely still fast.

reticivis
  • 489
  • 2
  • 7
  • 13
  • 2
    This is by far the best answer here. – OzzyCzech Mar 29 '23 at 09:11
  • I think this is the only correct answer. It's clean, it does all the work in machine-language. I would not even bother to create a function but just paste it in where I need it, so my colleagues can see it returns a number. – Joeri Jun 04 '23 at 11:45
5

PHP-Like rounding Method

The code below can be used to add your own version of Math.round to your own namespace which takes a precision parameter. Unlike Decimal rounding in the example above, this performs no conversion to and from strings, and the precision parameter works same way as PHP and Excel whereby a positive 1 would round to 1 decimal place and -1 would round to the tens.

var myNamespace = {};
myNamespace.round = function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
};

myNamespace.round(1234.5678, 1); // 1234.6
myNamespace.round(1234.5678, -1); // 1230

from Mozilla Developer reference for Math.round()

jaggedsoft
  • 3,858
  • 2
  • 33
  • 41
JohnnyBizzle
  • 971
  • 3
  • 17
  • 31
2

Hopefully working code (didn't do much testing):

function toFixed(value, precision) {
    var precision = precision || 0,
        neg = value < 0,
        power = Math.pow(10, precision),
        value = Math.round(value * power),
        integral = String((neg ? Math.ceil : Math.floor)(value / power)),
        fraction = String((neg ? -value : value) % power),
        padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');

    return precision ? integral + '.' +  padding + fraction : integral;
}
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • Your code has a bug. I tried toFixed(2.01, 4) and got a result of "2.100". If I ever find a way to fix it, I will post it as an answer to this question. – Elias Zamaria May 25 '10 at 23:28
  • @mikez302: the padding computation was off by one; should work now, but feel free to bug me again if it's still broken... – Christoph May 27 '10 at 14:32
  • Very strange. When I run this fuction with the firebug console open in firefox 17 it freezes the whole browser like js is caught in an endless loop. Even if i do not console.log the output. If I do not have firebug activated the bug does not occur. – Rick Kukiela Jan 07 '13 at 10:40
  • 1
    Update, i ran it in chrome and i get: Uncaught RangeError: Maximum call stack size exceeded in regards to the toFixed function call. – Rick Kukiela Jan 07 '13 at 10:42
  • @SublymeRick: I have no idea why this happens; shot in the dark: try renaming the function... – Christoph Jan 07 '13 at 11:01
  • @Christoph, I believe it is happening because of stacking all vars and calculations with comma on a single var statement. It breaks the debugger, but works in browser.I had similar case with a larger script, if I remember correctly on Firefox 42 – AaA Nov 06 '17 at 02:15
1

I think below function can help

function roundOff(value,round) {
   return (parseInt(value * (10 ** (round + 1))) - parseInt(value * (10 ** round)) * 10) > 4 ? (((parseFloat(parseInt((value + parseFloat(1 / (10 ** round))) * (10 ** round))))) / (10 ** round)) : (parseFloat(parseInt(value * (10 ** round))) / ( 10 ** round));
}

usage : roundOff(600.23458,2); will return 600.23

KRISHNA TEJA
  • 147
  • 1
  • 13
0
function round(number,decimals) {
  var value = Math.abs(number).toString().toLowerCase().split("e");
  var step = !value.includes('e') ? (value[0] + 'E' + decimals) : (value[0] + 'E' + (+value[1] + decimals));
  var sign = Math.sign(number)<0?"-":"";
  var exponent = (Math[(step % 2 <= .5 || (step % 2 >= 1 && step % 2 < 1.5))?'floor':'ceil'](typeof(value[1]) == 'undefined' ? (value[0] + 'E' + decimals) : (value[0] + 'E' + (+value[1] + decimals))))["toString"]().toLowerCase().split("e")[1];
  return sign + (Math.abs(number) >= 1E21 ? Math.abs(number)["toString"]().toLowerCase().split("e")[0].replace(".","").padEnd(+exponent-1,0) : +((Math[(step % 2 <= .5 || (step % 2 >= 1 && step % 2 < 1.5))?'floor':'ceil'](typeof(value[1]) == 'undefined' ? (value[0] + 'E' + decimals) : (value[0] + 'E' + (+value[1] + decimals))))["toString"]().toLowerCase().split("e")[0].replace('.','').padEnd(+exponent+1,0) + (decimals==0?"":"E" + -decimals)));
}

console.log(round(1.492,2)); // 1.49
console.log(round(1.499245,5)); // 1.49924
console.log(round(-3.0e-7,5)); // -0
console.log(round(-0.00000050001,6)); // -0.000001
-2

If you do not really care about rounding, just added a toFixed(x) and then removing trailing 0es and the dot if necessary. It is not a fast solution.

function format(value, decimals) {
    if (value) {
        value = value.toFixed(decimals);            
    } else {
        value = "0";
    }
    if (value.indexOf(".") < 0) { value += "."; }
    var dotIdx = value.indexOf(".");
    while (value.length - dotIdx <= decimals) { value += "0"; } // add 0's

    return value;
}
Juan Carrey
  • 696
  • 1
  • 6
  • 13