4063

I'd like to round at most two decimal places, but only if necessary.

Input:

10
1.7777777
9.1

Output:

10
1.78
9.1

How can I do this in JavaScript?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
stinkycheeseman
  • 43,437
  • 7
  • 30
  • 49
  • 1
    const formattedNumber = Math.round(myNumber * 100) / 100; – Hamza Dahmoun Oct 05 '22 at 17:28
  • ```const myNumber = 1.275; const formattedNumber = new Intl.NumberFormat('en', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(myNumber); const formattedNumberInNumber = parseFloat(formattedNumber);``` // #=> 1.28. [more](https://stackoverflow.com/a/1726662/18697532) – tinystone Mar 12 '23 at 09:38

91 Answers91

5266

Use Math.round() :

Math.round(num * 100) / 100

Or to be more specific and to ensure things like 1.005 round correctly, use Number.EPSILON :

Math.round((num + Number.EPSILON) * 100) / 100
user229044
  • 232,980
  • 40
  • 330
  • 338
Brian Ustas
  • 62,713
  • 3
  • 28
  • 21
  • Math.round((224.98499999 + Number.EPSILON) * 100) / 100 224.98 where as it should have ben 224.95 right? – Satish Patro Feb 05 '21 at 08:03
  • 10
    @PSatishPatro (I assume you meant to say 224.99 and not 224.95). if you're rounding to the 2nd decimal (hundredths) then we should only care about what number the 3rd (thousandth) decimal is and everything after that is dropped. So from the input 224.98499999, only 224.984 matters which means **224.98** is correct. – Francisc0 Feb 11 '21 at 18:37
  • yeah, 224.99 but when we do in pen-paper we don't consider not only 3rd in this scenario but after 3rd digits also. But, generally rounding mode is half up & if you do in java language also we will get 224.99 as that is the proper math wise. Is JS giving rounding to 224.98 because it is not considering half up or like you said only 3 digits considered (+1 digit)? – Satish Patro Feb 16 '21 at 10:14
  • There is a serious problem with using Number.EPSILON. I have a variable named value and it has the number 5. When I do value + Number.EPSILON I get the answer 52.220446049250313e-16. Which is really wrong – Martijn Hiemstra Mar 09 '21 at 09:16
  • 1
    I was able to find out what was going on. Turns out that Javascript saw value as a string and not a number. This really messes things up. Once Iconverted the string to a number using Number.parseFloat then it worked perfectly. – Martijn Hiemstra Mar 09 '21 at 09:22
  • 35
    Math.round(1.255 * 100) / 100 will be 1.25 . it's wrong – Sun Mar 23 '21 at 04:49
  • 7
    @PSatishPatro we're off-topic, I realise, but rounding 224.9849... to two decimal places should, in any language or by hand, result in 224.98. If you get 224.99 I'm afraid you did it wrong. The simplest way to think of it is that you're looking for the nearest number with only two decimal places. While there isn't much difference, 224.9849 is closer to 224.98 than to 224.99. – Blackbeard Mar 25 '21 at 20:28
  • Yeah, you are right, it's nearer to 224.98. We have been tought to do by last digit, if it is more than or eq to 5, add +1 to prev digit then do same thing till you need (which is rounding half up). if you go by that 224.9849 -> 224.985 -> 224.99. From this half up perspective I was asking http://www.c-jump.com/bcc/c157c/Week05/W05_0380_roundingmodehalfup.htm – Satish Patro Mar 29 '21 at 13:33
  • some here confuse truncate to round, truncate just cuts the number, round considers if the last digit is >= 5 to round up, ortherwise round down the next digit, then it trunctates, doing that until you get the extact number of digits needed. – Pablo Pazos May 06 '21 at 22:01
  • 2
    Not sure why this is marked as the correct answer. It does in fact not work correctly – JokerMartini May 27 '21 at 12:13
  • I'm not sure why you think it doesn't work. I tested it with the number "13.193445894894595" and It rounded totally fine. Additionally, to this very good answer, I want to write down my solution to programmatically get the number of decimal places by a parameter called `decimalPlace`. `const _decimalPlace = Number(String(1).padEnd(decimalPlace + 1, 0))`. I hope this small calculation helps those who need a generic function to round numbers. – phng May 28 '21 at 09:29
  • I made a function: function roundToDecimalPlaces(x,decimalPlaces) { if (decimalPlaces < 0) throw new Error("Negative decimal places."); const a = 10**decimalPlaces; const b = Math.round(x * a) / a; return b; } –  Jul 14 '21 at 20:49
  • 23
    I find that it rounds wrong for 10.075. Gives 10.07 rather than 10.08, even with the epsilon fix. – Nauraushaun Sep 02 '21 at 21:25
  • 15
    Math.round((519.805+ Number.EPSILON) * 100) / 100, it rounds to 519.8 – yuyicman Sep 03 '21 at 02:55
  • 2
    `Math.round((num + Number.EPSILON) * 100) / 100` In some cases, when you round number like 1.3549999999999998 it will return incorrect result. Should be 1.35 but result is 1.36. ref: https://stackoverflow.com/a/12830454/1407491 – Nabi K.A.Z. Apr 22 '22 at 15:27
  • 2
    This does not work, and cannot possibly work. Floating-point values don't have decimal places, they have binary places, and the two are incommensurable. – user207421 May 22 '22 at 04:22
  • `const round = (num: number, decimals: number) => { return Math.round((num + Number.EPSILON) * 10 ** decimals) / 10 ** decimals; };` Adapted function to round the number to any decimal numbers. – Yao Sep 11 '22 at 16:18
  • 1
    This is incorrect answer! It's not working with numbers like 10.075 or 1.3549999999999998 Only working solution is "Solution 1" from https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary?page=1&tab=trending#answer-12830454 – KubiQ Sep 30 '22 at 12:43
  • 4
    Surprising that this incorrect answer is upvoted so much. The following is a better answer: https://stackoverflow.com/a/29249277/908621 – fishinear Nov 10 '22 at 17:01
  • The solution I've come to is to do two `Math.round`s: `Math.round(Math.round(value * 1000) / 10) / 100`. The issue is with JavaScript sometimes making `value * 100` something that is less than the actual result, but if you multiply it with 1000 and round it, it will get a much more accurate result and then you divide it twice with an intermediary round. The more accurate you need to get the more 0s you need to add for the intermediary result. It works well if you know the maximum number of decimals. I've tested with 3 decimals from 1 to 100, it worked for all. – Andrei15193 Jan 17 '23 at 16:03
  • does not work for 35.855 – Simon Tran Jun 21 '23 at 15:14
  • 1
    As mentioned by @Amr Ali here https://stackoverflow.com/a/48764436/11249929, the best results you can get with this method is to mutliple by Number.EPSILON just before rounding: `Math.round((value * 100) * (1 + Number.EPSILON)) / 100` - this also nicely handles negative values out-of-the-box – Dominik Brázdil Aug 15 '23 at 08:51
4156

If the value is a text type:

parseFloat("123.456").toFixed(2);

If the value is a number:

var numb = 123.23454;
numb = numb.toFixed(2);

There is a downside that values like 1.5 will give "1.50" as the output. A fix suggested by @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

It seems like Math.round is a better solution. But it is not! In some cases it will not round correctly:

Math.round(1.005 * 100)/100 // Returns 1 instead of expected 1.01!

toFixed() will also not round correctly in some cases (tested in Chrome v.55.0.2883.87)!

Examples:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

I guess, this is because 1.555 is actually something like float 1.55499994 behind the scenes.

Solution 1 is to use a script with required rounding algorithm, for example:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

It is also at Plunker.

Note: This is not a universal solution for everyone. There are several different rounding algorithms. Your implementation can be different, and it depends on your requirements. See also Rounding.

Solution 2 is to avoid front end calculations and pull rounded values from the backend server.

Another possible solution, which is not a bulletproof either.

Math.round((num + Number.EPSILON) * 100) / 100

In some cases, when you round a number like 1.3549999999999998, it will return an incorrect result. It should be 1.35, but the result is 1.36.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
A Kunin
  • 42,385
  • 1
  • 17
  • 13
  • in this function `roundNumberV2` there is this condition `if (Math.pow(0.1, scale) > num) { return 0; }`. may I know what is the purpose of this condition ? – Mei Lie Aug 18 '21 at 11:57
  • Performance should be a concern also, which could make this approach less desirable. `Math.round()` is _much_ faster. https://jsbin.com/kikocecemu/edit?js,output – Matt Sep 28 '21 at 16:51
  • Note, as a heads-up for someone because this bit me, but if you want to do something like `var a = parseFloat(1/3).toFixed(2);` it doesn't seem to like it when you do `var c = a + someNumber;` - it will treat it like you are trying to add a string (that new `a` there) to a number (`someNumber`). So probably would need to do `var c = eval(a) + someNumber;`. – vapcguy Nov 24 '21 at 18:43
  • Note: "Executing JavaScript from a string is an enormous security risk. It is far too easy for a bad actor to run arbitrary code when you use eval()", see MDN docs [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) – Jayme Nov 25 '21 at 07:53
  • 4
    Instead of `eval(a)` you should use `Number(a)`, `parseFloat(a)` (which actually behave the same https://stackoverflow.com/a/11988612/16940). You can even just use `+a`. I prefer `Number(a)`. – Simon_Weaver Dec 30 '21 at 05:55
  • 2
    same problem as Ustas' suggestion. 10.075 input = 10.07 output. No good. – R-D May 18 '22 at 14:57
  • **Solution 1** works great @Nando Here is a shorter version which a) also handles uppercase "E" ("1.23E10" is valid input) b) handles negative `scale` ---- `function roundNumberV1(num, scale) { [ base, exp=0 ] = ("" + num).toLowerCase().split("e"); return +(Math.round(+base + "e" + (+exp + scale)) + "e" + -scale); }` – Joakim Feb 03 '23 at 23:00
  • that's a neat little trick to add `+` at the beginning - `+numb.toFixed(2)` – Gangula Mar 04 '23 at 09:49
636

I found this on MDN. Their way avoids the problem with 1.005 that was mentioned.

function roundToTwo(num) {
    return +(Math.round(num + "e+2")  + "e-2");
}

console.log('1.005 => ', roundToTwo(1.005));
console.log('10 => ', roundToTwo(10));
console.log('1.7777777 => ', roundToTwo(1.7777777));
console.log('9.1 => ', roundToTwo(9.1));
console.log('1234.5678 => ', roundToTwo(1234.5678));
console.log('1.3549999999999998 => ', roundToTwo(1.3549999999999998));
console.log('10.075 => ', roundToTwo(10.075));
mplungjan
  • 169,008
  • 28
  • 173
  • 236
MarkG
  • 7,670
  • 2
  • 16
  • 11
  • 21
    @Redsandro, `+(val)` is the coercion equivalent of using `Number(val)`. Concatenating "e-2" to an number resulted in a string that needed to be converted back to a number. – Jack Feb 28 '14 at 19:11
  • 10
    Pass a number with e and it returns NaN e.g. 1.19e-7 – Antony Ng Jan 08 '21 at 10:02
  • 4
    This does not work well for negative numbers. – Nabi K.A.Z. Apr 22 '22 at 15:49
  • 2
    However, if num is `-2.9e-7`, then `+(Math.round(num + "e+2") + "e-2") ` returns `NaN`, which is not the desired reult. At least on Chrome 101 – Zuabi May 20 '22 at 14:03
  • 2
    In case of -1.005 => -1 (without decimals) – tatactic Oct 12 '22 at 07:57
  • I found mdn en-US version didn't give example for decimal approximation. the example is in [here](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Math/round#%E5%8D%81%E9%80%B2%E4%BD%8D%E8%BF%91%E4%BC%BC%E5%80%BC). test result: `Math.round10(1.005,-2) => 1.01;` `Math.round10(-1.005,-2) => -1;` `Math.round10(-2.9e-7,-2) => 0;` `Math.round10(-2.9e-7,-7) => -3e-7;` – bigiCrab Jul 26 '23 at 07:13
208

MarkG's answer is the correct one. Here's a generic extension for any number of decimal places.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Usage:

var n = 1.7777;    
n.round(2); // 1.78

Unit test:

it.only('should round floats to 2 places', function() {
    
  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]
    
  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lavamantis
  • 3,766
  • 2
  • 25
  • 22
  • I find this standalone (no `prototype` extension) version (ES6) easy to read and straight forward: `round = (num, precision) => Number(Math.round(num + "e+" + precision) + "e-" + precision);` – Dut A. Feb 01 '21 at 00:04
  • 7
    What if the input number is already in exponential form? You will get NaN – Learner Mar 05 '21 at 07:50
  • 1
    I receive this error in this (Math.round(number + "e+" + places)) Argument of type 'string' is not assignable to parameter of type 'number' In Typescript – juanjinario Jun 16 '21 at 09:36
  • @Learner If the input is already in exponential form, you can convert it to decimal form first with `n = n.toFixed(20)`, where 20 is the maximum precision of that method (so you can't round past 20 digits). – kennysong Oct 14 '21 at 01:35
  • 2
    to accomodate for very small and very large number which will be in exponential form automatically you can address that with toFixed. I.e. `function round(val, decimals) { return +(Math.round(+(val.toFixed(decimals) + "e+" + decimals)) + "e-" + decimals); }` – Per Nov 26 '21 at 19:34
  • 11
    oh come on dont modify prototypes – Delight Dec 15 '21 at 21:27
197

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 = 0) {
    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)

// testing edge cases
console.log( naiveRound(1.005, 2) );  // 1    incorrect (should be 1.01)
console.log( naiveRound(2.175, 2) );  // 2.17 incorrect (should be 2.18)
console.log( naiveRound(5.015, 2) );  // 5.01 incorrect (should be 5.02)

In order to determine whether a rounding operation involves a midpoint value, the Round function multiplies the original value to be rounded by 10 ** n, where n is the desired number of fractional digits in the return value, and then determines whether the remaining fractional portion of the value is greater than or equal to .5. This "Exact Testing for Equality" with floating-point values are problematic because of the floating-point format's issues with binary representation and precision. This means that any fractional portion of a number that is slightly less than .5 (because of a loss of precision) will not be rounded upward.

In the previous example, 5.015 is a midpoint value if it is to be rounded to two decimal places, the value of 5.015 * 100 is actually 501.49999999999994. Because .49999999999994 is less than .5, it is rounded down to 501 and finally the result is 5.01.

Better implementations

Exponential notation

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')
 * Negative numbers round differently than positive numbers.
 */
function round(num, decimalPlaces = 0) {
    num = Math.round(num + "e" + decimalPlaces);
    return Number(num + "e" + -decimalPlaces);
}

// test rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // 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

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 = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);

    num = Math.round(num + "e" + decimalPlaces);
    return Number(num + "e" + -decimalPlaces);
}

Approximate rounding

To correct the rounding problem shown in the previous naiveRound example, we can define a custom rounding function that performs a "nearly equal" test to determine whether a fractional value is sufficiently close to a midpoint value to be subject to midpoint rounding.

// round half away from zero
function round(num, decimalPlaces = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);
    var p = Math.pow(10, decimalPlaces);
    var n = num * p;
    var f = n - Math.floor(n);
    var e = Number.EPSILON * n;

    // Determine whether this fraction is a midpoint value.
    return (f >= .5 - e) ? Math.ceil(n) / p : Math.floor(n) / p;
}

// test rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -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

Number.EPSILON

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 product before rounding. This moves to the next representable float value, away from zero, thus it will offset the binary round-off error that may occur during the multiplication by 10 ** n.

/**
 * 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 = 0) {
    var p = Math.pow(10, decimalPlaces);
    var n = (num * p) * (1 + Number.EPSILON);
    return Math.round(n) / p;
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -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

After adding 1 ulp, the value of 5.015 * 100 which is 501.49999999999994 will be corrected to 501.50000000000006, this will rounded up to 502 and finally the result is 5.02.

Note that the size of a unit in last place ("ulp") is determined by (1) the magnitude of the number and (2) the relative machine epsilon (2^-52). Ulps are relatively larger at numbers with bigger magnitudes than they are at numbers with smaller magnitudes.

Double rounding

Here, we use the toPrecision() method to strip the floating-point round-off errors in the intermediate calculations. Simply, we round to 15 significant figures to strip the round-off error at the 16th significant digit. This technique to preround the result to significant digits is also used by PHP 7 round function.

The value of 5.015 * 100 which is 501.49999999999994 will be rounded first to 15 significant digits as 501.500000000000, then it will rounded up again to 502 and finally the result is 5.02.

// Round half away from zero
function round(num, decimalPlaces = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);
    var p = Math.pow(10, decimalPlaces);
    var n = (num * p).toPrecision(15);
    return Math.round(n) / p;
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -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

Arbitrary-precision JavaScript library - decimal.js

// Round half away from zero
function round(num, decimalPlaces = 0) {
    return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber();
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -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
<script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.1/decimal.js" integrity="sha512-GKse2KVGCCMVBn4riigHjXE8j5hCxYLPXDw8AvcjUtrt+a9TbZFtIKGdArXwYOlZvdmkhQLWQ46ZE3Q1RIa7uQ==" crossorigin="anonymous"></script>

Solution 1: string in exponential notation

Inspired by the solution provided by KFish here: https://stackoverflow.com/a/55521592/4208440

A simple drop in solution that provides accurate decimal rounding, flooring, and ceiling to a specific number of decimal places without adding a whole library. It treats floats more like decimals by fixing the binary rounding issues to avoid unexpected results: for example, floor((0.1+0.7)*10) will return the expected result 8.

Numbers are rounded to a specific number of fractional digits. Specifying a negative precision will round to any number of places to the left of the decimal point.

// Solution 1
var DecimalPrecision = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var shift = function(value, exponent) {
            value = (value + 'e').split('e');
            return +(value[0] + 'e' + (+value[1] + (exponent || 0)));
        };
        var n = shift(num, +decimalPlaces);
        return shift(Math[type](n), -decimalPlaces);
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision.round(0.5));  // 1
console.log(DecimalPrecision.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision.round(5.12, 1) === 5.1);
console.log(DecimalPrecision.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision.round(1.005, 2) === 1.01);
console.log(DecimalPrecision.round(39.425, 2) === 39.43);
console.log(DecimalPrecision.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision.round(1262.48, -1) === 1260);
console.log(DecimalPrecision.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision.toFixed(1.005, 2) === "1.01");

Solution 2: purely mathematical (Number.EPSILON)

This solution avoids any string conversion / manipulation of any kind for performance reasons.

// Solution 2
var DecimalPrecision2 = (function() {
    if (Number.EPSILON === undefined) {
        Number.EPSILON = Math.pow(2, -52);
    }
    if (Math.sign === undefined) {
        Math.sign = function(x) {
            return ((x > 0) - (x < 0)) || +x;
        };
    }
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 + Number.EPSILON);
            return Math.round(n) / p;
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 - Math.sign(num) * Number.EPSILON);
            return Math.ceil(n) / p;
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 + Math.sign(num) * Number.EPSILON);
            return Math.floor(n) / p;
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return (num < 0 ? this.ceil : this.floor)(num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return this.round(num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision2.round(0.5));  // 1
console.log(DecimalPrecision2.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision2.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision2.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision2.round(5.12, 1) === 5.1);
console.log(DecimalPrecision2.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision2.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision2.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision2.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision2.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision2.round(1.005, 2) === 1.01);
console.log(DecimalPrecision2.round(39.425, 2) === 39.43);
console.log(DecimalPrecision2.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision2.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision2.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision2.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision2.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision2.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision2.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision2.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision2.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision2.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision2.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision2.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision2.round(1262.48, -1) === 1260);
console.log(DecimalPrecision2.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision2.toFixed(1.005, 2) === "1.01");

Solution 3: double rounding

This solution uses the toPrecision() method to strip the floating-point round-off errors.

// Solution 3
var DecimalPrecision3 = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var powers = [
        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
        1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
        1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
    ];
    var intpow10 = function(power) {
        /* Not in lookup table */
        if (power < 0 || power > 22) {
            return Math.pow(10, power);
        }
        return powers[power];
    };
    // Eliminate binary floating-point inaccuracies.
    var stripError = function(num) {
        if (Number.isInteger(num))
            return num;
        return parseFloat(num.toPrecision(15));
    };
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var p = intpow10(decimalPlaces || 0);
        var n = stripError(num * p);
        return Math[type](n) / p;
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision3.round(0.5));  // 1
console.log(DecimalPrecision3.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision3.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision3.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision3.round(5.12, 1) === 5.1);
console.log(DecimalPrecision3.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision3.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision3.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision3.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision3.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision3.round(1.005, 2) === 1.01);
console.log(DecimalPrecision3.round(39.425, 2) === 39.43);
console.log(DecimalPrecision3.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision3.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision3.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision3.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision3.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision3.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision3.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision3.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision3.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision3.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision3.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision3.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision3.round(1262.48, -1) === 1260);
console.log(DecimalPrecision3.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision3.toFixed(1.005, 2) === "1.01");

Solution 4: double rounding v2

This solution is just like Solution 3, however it uses a custom toPrecision() function.

// Solution 4
var DecimalPrecision4 = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var powers = [
        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
        1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
        1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
    ];
    var intpow10 = function(power) {
        /* Not in lookup table */
        if (power < 0 || power > 22) {
            return Math.pow(10, power);
        }
        return powers[power];
    };
    var toPrecision = function(num, significantDigits) {
        // Return early for ±0, NaN and Infinity.
        if (!num || !Number.isFinite(num))
            return num;
        // Compute shift of the decimal point (sf - leftSidedDigits).
        var shift = significantDigits - 1 - Math.floor(Math.log10(Math.abs(num)));
        // Return if rounding to the same or higher precision.
        var decimalPlaces = 0;
        for (var p = 1; num != Math.round(num * p) / p; p *= 10) decimalPlaces++;
        if (shift >= decimalPlaces)
            return num;
        // Round to "shift" fractional digits
        var scale = intpow10(Math.abs(shift));
        return shift > 0 ?
            Math.round(num * scale) / scale :
            Math.round(num / scale) * scale;
    };
    // Eliminate binary floating-point inaccuracies.
    var stripError = function(num) {
        if (Number.isInteger(num))
            return num;
        return toPrecision(num, 15);
    };
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var p = intpow10(decimalPlaces || 0);
        var n = stripError(num * p);
        return Math[type](n) / p;
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision4.round(0.5));  // 1
console.log(DecimalPrecision4.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision4.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision4.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision4.round(5.12, 1) === 5.1);
console.log(DecimalPrecision4.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision4.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision4.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision4.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision4.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision4.round(1.005, 2) === 1.01);
console.log(DecimalPrecision4.round(39.425, 2) === 39.43);
console.log(DecimalPrecision4.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision4.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision4.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision4.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision4.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision4.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision4.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision4.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision4.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision4.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision4.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision4.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision4.round(1262.48, -1) === 1260);
console.log(DecimalPrecision4.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision4.toFixed(1.005, 2) === "1.01");

Benchmarks

http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac

Here is a benchmark comparing the operations per second in the solutions above on Chrome 109.0.0.0. Rounding functions using the Number.EPSILON is at least 10x-20x faster. Obviously all browsers differ, so your mileage may vary.

Benchmark comparison (Note: More is better)

Thanks @Mike for adding a screenshot of the benchmark.

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Amr Ali
  • 3,020
  • 1
  • 16
  • 11
  • Nice, I see that you did run a more thorough performance test on the difference. I just did a quick comparison in devtools and they came back with very similar variance in execution time, but I was wondering if the performance difference would begin to show at a really high volume/frequency. – KFish Aug 24 '20 at 13:58
  • The second solution is easier to read and understand. Also it can be ported to other programming languages. – Amr Ali Aug 25 '20 at 15:54
  • 4
    Hey @AmrAli. This is an awesome answer. One of the few that are as accurate as possible. Thanks! I particularly like `Solution 2` for it's speed. One thing I noticed is the speed can be increased by ~5-10% if the early-return check for `isRound` is removed. It adds more operations than just running the `decimalAdjust` function. Returning early by using isRound actually takes longer. – GollyJer Oct 16 '20 at 05:47
  • Hi @GollyJer. The check for `isRound` is needed for edge cases with `ceil, floor and trunc`. It is actually not needed for `round`. – Amr Ali Oct 17 '20 at 09:44
  • Ha. That makes sense. Round is the only function I was concerned with at the time. I edited my version of the early return in `decimalAdjust` to `if (type === 'round' || isRound(num, decimalPlaces || 0))` to benefit from the speedup. Thanks for the response! – GollyJer Oct 19 '20 at 02:58
  • 2
    I've looked through many solutions on StackOverflow and this one is the best. The Exponential notation solution with the mod for negative numbers seems to work best for currency and matches the Java round calculations on the backend. – Locutus Jan 22 '21 at 23:30
  • This is by far the best solution, and overall demonstration of solving a problem correctly. I came back here because someone liked my answer, but I hope they took my advice to refer to this answer. Honestly, hats off to @AmrAli for dedicating the time to stick to this until it could be solved to this degree of precision (pun intended). If everyone put this much into every dev project a whole LOT of issues could be avoided. This should replace the accepted answer, and really should be pinned on SO to just put this in front of anyone that goes anywhere near the rabbit hole of decimal precision. – KFish Mar 10 '21 at 04:39
  • Can I have the Number Epsilon solution in typescript? I found it hard to write it in typescript. For starters `Number.EPSILON = Math.pow(2, -52)` doesn't work. Or maybe I can just ignore this line? – Patronaut Dec 03 '21 at 19:08
  • In typescript: `const EPSILON: number = Math.pow(2, -52);` – Amr Ali Dec 03 '21 at 22:13
  • to Solution 1: `console.log(DecimalPrecision.toFixed(37.8/86.4,3));` wrong output: `"0.437"`, right output need to be: `0.438` – Hristian Yordanov Mar 07 '22 at 07:36
  • I really don't know the exact mechnism of how javascript performs calculations with exponential notation. But, I can confirm that the other 3 solutions work as expected. – Amr Ali Mar 07 '22 at 20:01
  • @Amr Ali you are right with Solution 2, there is no issue with my sample. Thanks for sharing! – Hristian Yordanov Mar 08 '22 at 08:49
  • In JavaScript `Math.round(-0.5)` is `-0` and not `-1` as returned by these functions. – Florat Apr 06 '22 at 07:54
  • The rounding mode used here is "round half away from zero", known also as commercial rounding. JS uses "round half up" mode. – Amr Ali Apr 07 '22 at 19:40
  • 2
    This answer is a very good example why you shouldn't just check the first comment in stackoverflow. Those 2 above are just simply wrong. – pbialy Apr 28 '22 at 13:40
  • 1
    Instead of providing a benchmark you should have run a test showing whether any of these techniques actually works, for say *0.0001 < x < 0.9999*. You might get a surprise how many of them fail. Over 90%. – user207421 May 22 '22 at 04:25
  • Fantastic ;) Your solutions work fine for 2777.77499999999996 and are rounded to 2777.78, where other solutions give me an incorrect 2777.77 – Piosek Aug 24 '22 at 08:08
  • round(39.425, 2) should be 39.42 not 39.43, round(-1.005, 2) should be -1 not -1.01. https://www.wolframalpha.com/input?i=round+2.545+to+2+decimals https://www.wolframalpha.com/input?i=round+2.555+to+2+decimals When between values it should always round to even number, not odd. – radulle Oct 12 '22 at 05:47
  • Rounding half to even is the most common rounding method and it is used in IEEE 754 https://en.wikipedia.org/wiki/Rounding#Round_half_to_even – radulle Oct 12 '22 at 05:51
  • Naadi Narambellaam programming/maths Veri yerunavan thaan ippudi oru answer poda mudiyum @AmrAli thanks – Karthikeyan Apr 03 '23 at 16:30
  • Posts like this show how silly JavaScipt can be, just to round a number. Why doesn't Math.round() accept a 2nd arg for the number of places to round like to like any sensible language? – run_the_race May 23 '23 at 14:27
189

You should use:

Math.round( num * 100 + Number.EPSILON ) / 100

No one seems to be aware of Number.EPSILON.

Also it's worth noting that this is not a JavaScript weirdness like some people stated.

That is simply the way floating point numbers works in a computer. Like 99% of programming languages, JavaScript doesn't have home made floating point numbers; it relies on the CPU/FPU for that. A computer uses binary, and in binary, there isn't any numbers like 0.1, but a mere binary approximation for that. Why? For the same reason than 1/3 cannot be written in decimal: its value is 0.33333333... with an infinity of threes.

Here come Number.EPSILON. That number is the difference between 1 and the next number existing in the double precision floating point numbers. That's it: There is no number between 1 and 1 + Number.EPSILON.

EDIT:

As asked in the comments, let's clarify one thing: adding Number.EPSILON is relevant only when the value to round is the result of an arithmetic operation, as it can swallow some floating point error delta.

It's not useful when the value comes from a direct source (e.g.: literal, user input or sensor).

EDIT (2019):

Like @maganap and some peoples have pointed out, it's best to add Number.EPSILON before multiplying:

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (december 2019):

Lately, I use a function similar to this one for comparing numbers epsilon-aware:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

My use-case is an assertion + data validation lib I'm developing for many years.

In fact, in the code I'm using ESPILON_RATE = 1 + 4 * Number.EPSILON and EPSILON_ZERO = 4 * Number.MIN_VALUE (four times the epsilon), because I want an equality checker loose enough for cumulating floating point error.

So far, it looks perfect for me. I hope it will help.

cronvel
  • 4,045
  • 2
  • 14
  • 19
  • Should I use 1000 instead of 100 if I want to round to 3 decimal numbers? – AliN11 Jan 31 '21 at 17:55
  • Math.round((224.98499999 * 100 + Number.EPSILON)) / 100 224.98 Instead of 224.99 – Satish Patro Feb 05 '21 at 08:05
  • @PSatishPatro That is correct. .849 is closer to .8 than it is to .9, thus, it's rounded down to .8. – Random Elephant May 11 '21 at 10:19
  • @RandomElephant, okay, but generally when we calculate we do rounding up which is rounding HALF UP from the last digit. 98499 -> .9849 -> .985 -> .99 .Is there any way to achieve this in js? – Satish Patro May 13 '21 at 08:17
  • 1
    @PSatishPatro There is, but it's incorrect math. There's no general rounding up where you start from the last digit, and if you do, you seriously need to consider re-learning maths. Edit: To answer, you'd take the length of the number digits, and looped them from the last one, rounding each one and changing the intial number until you got to the desired place count. – Random Elephant May 13 '21 at 08:54
  • you are right, scale 2 with rounding half up, gave 224.98, but when number is 224.985, it gave 224.99. I was doing it wrong it seems. Thank you – Satish Patro May 14 '21 at 11:05
  • I don't any comment about putting EPSILON before multiplication. Why is that? It fails for 1.3549999999999998. – Andre Figueiredo Dec 07 '21 at 01:18
  • 10.075 input returns 10.07 output. No good. – R-D May 18 '22 at 15:00
  • Does not work for 35.855 – Simon Tran Jun 21 '23 at 15:17
122

This question is complicated.

Suppose we have a function, roundTo2DP(num), that takes a float as an argument and returns a value rounded to 2 decimal places. What should each of these expressions evaluate to?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

The 'obvious' answer is that the first example should round to 0.01 (because it's closer to 0.01 than to 0.02) while the other two should round to 0.02 (because 0.0150000000000000001 is closer to 0.02 than to 0.01, and because 0.015 is exactly halfway between them and there is a mathematical convention that such numbers get rounded up).

The catch, which you may have guessed, is that roundTo2DP cannot possibly be implemented to give those obvious answers, because all three numbers passed to it are the same number. IEEE 754 binary floating point numbers (the kind used by JavaScript) can't exactly represent most non-integer numbers, and so all three numeric literals above get rounded to a nearby valid floating point number. This number, as it happens, is exactly

0.01499999999999999944488848768742172978818416595458984375

which is closer to 0.01 than to 0.02.

You can see that all three numbers are the same at your browser console, Node shell, or other JavaScript interpreter. Just compare them:

> 0.014999999999999999 === 0.0150000000000000001
true

So when I write m = 0.0150000000000000001, the exact value of m that I end up with is closer to 0.01 than it is to 0.02. And yet, if I convert m to a String...

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

... I get 0.015, which should round to 0.02, and which is noticeably not the 56-decimal-place number I earlier said that all of these numbers were exactly equal to. So what dark magic is this?

The answer can be found in the ECMAScript specification, in section 7.1.12.1: ToString applied to the Number type. Here the rules for converting some Number m to a String are laid down. The key part is point 5, in which an integer s is generated whose digits will be used in the String representation of m:

let n, k, and s be integers such that k ≥ 1, 10k-1s < 10k, the Number value for s × 10n-k is m, and k is as small as possible. Note that k is the number of digits in the decimal representation of s, that s is not divisible by 10, and that the least significant digit of s is not necessarily uniquely determined by these criteria.

The key part here is the requirement that "k is as small as possible". What that requirement amounts to is a requirement that, given a Number m, the value of String(m) must have the least possible number of digits while still satisfying the requirement that Number(String(m)) === m. Since we already know that 0.015 === 0.0150000000000000001, it's now clear why String(0.0150000000000000001) === '0.015' must be true.

Of course, none of this discussion has directly answered what roundTo2DP(m) should return. If m's exact value is 0.01499999999999999944488848768742172978818416595458984375, but its String representation is '0.015', then what is the correct answer - mathematically, practically, philosophically, or whatever - when we round it to two decimal places?

There is no single correct answer to this. It depends upon your use case. You probably want to respect the String representation and round upwards when:

  • The value being represented is inherently discrete, e.g. an amount of currency in a 3-decimal-place currency like dinars. In this case, the true value of a Number like 0.015 is 0.015, and the 0.0149999999... representation that it gets in binary floating point is a rounding error. (Of course, many will argue, reasonably, that you should use a decimal library for handling such values and never represent them as binary floating point Numbers in the first place.)
  • The value was typed by a user. In this case, again, the exact decimal number entered is more 'true' than the nearest binary floating point representation.

On the other hand, you probably want to respect the binary floating point value and round downwards when your value is from an inherently continuous scale - for instance, if it's a reading from a sensor.

These two approaches require different code. To respect the String representation of the Number, we can (with quite a bit of reasonably subtle code) implement our own rounding that acts directly on the String representation, digit by digit, using the same algorithm you would've used in school when you were taught how to round numbers. Below is an example which respects the OP's requirement of representing the number to 2 decimal places "only when necessary" by stripping trailing zeroes after the decimal point; you may, of course, need to tweak it to your precise needs.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }
    if (num[0] == '-') {
        return "-" + roundStringNumberWithoutTrailingZeroes(num.slice(1), dp)
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        // We want to do this while still avoiding converting the whole
        // beforePart to a Number (since that could cause loss of precision if
        // beforePart is bigger than Number.MAX_SAFE_INTEGER), so the logic for
        // this is once again kinda complicated.
        // Note we can (and want to) use early returns here because the
        // zero-stripping logic at the end of
        // roundStringNumberWithoutTrailingZeroes does NOT apply here, since
        // the result is a whole number.
        if (/^9+$/.test(beforePoint)) {
            return "1" + beforePoint.replaceAll("9", "0")
        }
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = beforePoint.length - 1;
        while (true) {
            if (beforePoint[i] == '9') {
                beforePoint = beforePoint.substr(0, i) +
                             '0' +
                             beforePoint.substr(i+1);
                i--;
            } else {
                beforePoint = beforePoint.substr(0, i) +
                             (Number(beforePoint[i]) + 1) +
                             beforePoint.substr(i+1);
                break;
            }
        }
        return beforePoint
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Example usage:

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'
> roundStringNumberWithoutTrailingZeroes('16.996', 2)
'17'

The function above is probably what you want to use to avoid users ever witnessing numbers that they have entered being rounded wrongly.

(As an alternative, you could also try the round10 library which provides a similarly-behaving function with a wildly different implementation.)

But what if you have the second kind of Number - a value taken from a continuous scale, where there's no reason to think that approximate decimal representations with fewer decimal places are more accurate than those with more? In that case, we don't want to respect the String representation, because that representation (as explained in the spec) is already sort-of-rounded; we don't want to make the mistake of saying "0.014999999...375 rounds up to 0.015, which rounds up to 0.02, so 0.014999999...375 rounds up to 0.02".

Here we can simply use the built-in toFixed method. Note that by calling Number() on the String returned by toFixed, we get a Number whose String representation has no trailing zeroes (thanks to the way JavaScript computes the String representation of a Number, discussed earlier in this answer).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
107

Consider .toFixed() and .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml

Bibhas Debnath
  • 14,559
  • 17
  • 68
  • 96
AceCorban
  • 2,023
  • 1
  • 15
  • 15
  • In firefox, `3.9935.toFixed(3) → "3.994"`, `3.9945.toFixed(3) → "3.994"`, `3.9955.toFixed(3) → "3.995"`, `3.9965.toFixed(3) → "3.997"`. Is it expected behavior? For example, shouldn't `3.9945.toFixed(3)` return `"3.995"` or `3.9955.toFixed(3)` return `"3.996"`? – Yeheshuah Jun 22 '21 at 06:50
  • 1
    [A Kunin](https://stackoverflow.com/users/1736537/a-kunin) has told a bit about this at below answer. – Yeheshuah Jun 22 '21 at 07:03
  • toFixed() sometimes doesn't round correctly. I've seen it myself. Math.round is better – Manny Alvarado Feb 04 '22 at 18:26
91

One can use .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23
Ninjakannon
  • 3,751
  • 7
  • 53
  • 76
Gourav Singla
  • 1,728
  • 1
  • 15
  • 22
  • 2
    This is a duplicate of [user3711536's answer](https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary/24406175#24406175)—though equally without any explanation whatsoever or link to documentation. At least the other answer had more sample input and output. – Peter Mortensen May 05 '22 at 16:30
  • does not trim zeros – serge Jul 27 '22 at 09:53
66

Here is a simple way to do it:

Math.round(value * 100) / 100

You might want to go ahead and make a separate function to do it for you though:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

Then you would simply pass in the value.

You could enhance it to round to any arbitrary number of decimals by adding a second parameter.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}
Bibhas Debnath
  • 14,559
  • 17
  • 68
  • 96
JayDM
  • 1,166
  • 9
  • 16
  • Here's a brief video tutorial [how to round to 2 decimal in js](https://www.youtube.com/watch?v=_vVwYgF_0w4) Wouldn't it be easier to just use the built-in toFixed(N) method? – InfiniteStack Jun 04 '22 at 15:53
66

None of the answers found here is correct. stinkycheeseman asked to round up, but you all rounded the number.

To round up, use this:

Math.ceil(num * 100)/100;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
machineaddict
  • 3,216
  • 8
  • 37
  • 61
65

A precise rounding method. Source: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(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));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Examples:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user
  • 17,781
  • 20
  • 98
  • 124
39

This may help you:

var result = Math.round(input*100)/100;

For more information, you can have a look at Math.round(num) vs num.toFixed(0) and browser inconsistencies

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
totten
  • 2,769
  • 3
  • 27
  • 41
  • 4
    Why in the world does the accepted answer have so many more votes than this one since they're practically the same thing, but this one was posted 1 minute after the accepted one? – DUO Labs Jan 25 '20 at 01:10
  • 2
    Math.round(1.965 * 100) / 100 will be 1.96 . it's wrong. – Kamlesh May 04 '22 at 12:09
  • They were roughly identical when created. The first substantial edit of the accepted answer was in 2020, while this answer was edited to include extra information 9 minutes after posted. So if this answer was wrong at creation, the accepted answer was wrong for the next 8 years. – Chris Strickland Oct 06 '22 at 00:04
38

If you are using the Lodash library, you can use the round method of Lodash like following.

_.round(number, precision)

For example:

_.round(1.7777777, 2) = 1.78
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Madura Pradeep
  • 2,378
  • 1
  • 30
  • 34
  • @Peter The set of functionalities that Lodash provide is really good compared to standard Javascript. However, I heard that Lodash has some performance issue with compare to standard JS. https://codeburst.io/why-you-shouldnt-use-lodash-anymore-and-use-pure-javascript-instead-c397df51a66 – Madura Pradeep Oct 04 '18 at 08:26
  • 2
    I accept your point that there are performance drawbacks with using lodash. I think that those issues are common to many abstractions. But just look at how many answers there are on this thread and how the intuitive solutions fail for edge cases. We have seen this pattern with jQuery and the root problem was solved when browsers adopted a common standard that solved most of our use cases. Performance bottlenecks were then moved to the browser engines. I think the same should happen to lodash. :) – Peter Oct 04 '18 at 10:17
37

Use this function Number(x).toFixed(2);

dmarcosl
  • 15
  • 6
Harish.bazee
  • 638
  • 6
  • 8
  • 9
    Wrap it all in `Number` again, if you don't want it returned as a string: `Number(Number(x).toFixed(2));` –  Sep 12 '15 at 05:17
  • 5
    The `Number` call is not necessary, `x.toFixed(2)` works. – bgusach Dec 14 '18 at 16:09
  • 3
    @bgusach Number call needed, since the statement x.toFixed(2) return string and not a number. To convert again to number we need to wrap with Number – Mohan Ram Mar 15 '19 at 06:26
  • 2
    When using this method `(1).toFixed(2)` returns `1.00`, but questioner needed `1` in this case. – Eugene Mala Jun 07 '19 at 20:40
  • 2
    This doesn't work, `1.005.toFixed(2)` yields `"1"` when it should be `"1.01"`. – Adam Jagosz Aug 28 '19 at 14:00
  • 1
    This is a duplicate of [user3711536's answer](https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary/24406175#24406175)—though equally without any explanation whatsoever. At least the other answer had some sample input and output. – Peter Mortensen May 05 '22 at 16:26
37

For me Math.round() was not giving correct answer. I found toFixed(2) works better. Below are examples of both:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
Vikasdeep Singh
  • 20,983
  • 15
  • 78
  • 104
  • Important to note that toFixed does not perform a rounding, and that Math.round just rounds to the nearest whole number. To preserve the decimals we therefore need to multiply the original number by the number of powers of ten whose zeros representing your desired number of decimals, and then divide the result by the same number. In your case: Math.round(43000 / 80000 * 100 * 100) / 100. At last toFixed(2) may be applied in order to ensure that there is always two decimals in the result (with trailing zeros where needed) – perfect for right-aligning a series of numbers presented vertically :) – Turbo May 01 '18 at 14:26
36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12
user3711536
  • 399
  • 3
  • 6
  • An explanation would be in order. E.g., what is the idea/gist? Why is toFixed() all there is to it? Is it from a particular library? What version of JavaScript/when was it introduced? From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/24406175/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen May 05 '22 at 16:12
  • OK, the OP has left the building. Perhaps someone else can chime in? – Peter Mortensen May 05 '22 at 16:15
35

Try this lightweight solution:

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2);
 // 1.22
 round(1.222, 10);
 // 1.222
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
petermeissner
  • 12,234
  • 5
  • 63
  • 63
  • Anyone know if there's any difference between this and `return Number(x.toFixed(digits))`? –  Sep 12 '15 at 05:16
  • 1
    @JoeRocc ... should make no difference as far a I can see since `.toFixed()` allows only for numbers anyways . – petermeissner Sep 12 '15 at 10:16
  • 4
    This answer has the same problem as mentioned several times on this page. Try `round(1.005, 2)` and see a result of `1` instead of `1.01`. – MilConDoin Aug 05 '16 at 06:54
  • seems more a problem of the rounding algo? - there are more than one would imagine: https://en.wikipedia.org/wiki/Rounding ... `round(0.995, 2) => 0.99`; `round(1.006, 2) => 1.01` ; `round(1.005, 2) => 1` – petermeissner Feb 07 '18 at 05:35
  • This works, but it adds unnecessary complexity to the system as converts a float into a string and then parses the string back to a float. – racribeiro Dec 29 '21 at 12:33
33

There are a couple of ways to do that. For people like me, Lodash's variant

function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

Usage:

round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

If your project uses jQuery or Lodash, you can also find the proper round method in the libraries.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
stanleyxu2005
  • 8,081
  • 14
  • 59
  • 94
  • The second option will return a string with exactly two decimal points. The question asks for decimal points only if necessary. The first option is better in this case. – Marcos Lima Jul 05 '16 at 13:36
  • @MarcosLima `Number.toFixed()` will return a string but with a plus symbol before it, JS interpreter will convert the string to a number. This is a syntax sugar. – stanleyxu2005 Jul 05 '16 at 14:04
  • On Firefox, `alert((+1234).toFixed(2))` shows "1234.00". – Marcos Lima Jul 05 '16 at 14:11
  • On Firefox, `alert(+1234.toFixed(2))` throws `SyntaxError: identifier starts immediately after numeric literal`. I stick with the 1st option. – Marcos Lima Jul 05 '16 at 15:19
  • This doesn't work in some edge cases: try ([jsfiddle](https://jsfiddle.net/wxrmoc53/)) with `362.42499999999995`. Expected result (as in PHP `echo round(362.42499999999995, 2)`): `362.43`. Actual result: `362.42` – Dr. Gianluigi Zane Zanettini Dec 06 '17 at 13:42
  • @Dr.GianluigiZaneZanettini Forget any programming language. If you do rounding manually, which result do you think is correct, `362.42` or `362.43`? I did some tests in PHP, I think it is a float number precision problem with PHP. If you run `echo(362.42499999999995);` you will get `362.425` and PHP use this number to round by 2, it returns `362.43`. I think this is a PHP problem, not a mathematical problem. – stanleyxu2005 Dec 07 '17 at 05:02
32

2017
Just use native code .toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

If you need to be strict and add digits just if needed it can use replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');
pery mimon
  • 7,713
  • 6
  • 52
  • 57
  • 4
    The toFixed method returns a string. If you want a number result you'll need to send the result of toFixed to parseFloat. – Zambonilli Nov 07 '17 at 20:04
  • 1
    @Zambonilli Or just multiply by 1 if it necessary. but because fixed number most cases are for display and not for calculation string is the right format – pery mimon Nov 08 '17 at 13:05
  • 3
    -1; not only was `toFixed` suggested by multiple answers years before yours, but it fails to satisfy the "only if necessary" condition in the question; `(1).toFixed(2)` gives `"1.00"` where the asker desired `"1"`. – Mark Amery Dec 07 '17 at 00:02
  • Ok got it. I add some solution also for that case – pery mimon Dec 14 '17 at 17:05
  • If you're using lodash, it's even easier: _.round(number, decimalPlace) Deleted my last comment, cuz it has an issue. Lodash _.round DOES work, though. 1.005 with decimal place of 2 converts to 1.01. – Devin Fields Jul 31 '18 at 17:40
  • But 1.005 should be rounded to 1.01. that is the rule of roundes – pery mimon Aug 02 '18 at 10:52
32

Another simple solution (without writing any function) may to use toFixed() and then convert to float again:

For example:

var objNumber = 1201203.1256546456;
objNumber = parseFloat(objNumber.toFixed(2))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
29

Since ES6 there is a 'proper' way (without overriding statics and creating workarounds) to do this by using toPrecision

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

then you can just parseFloat and zeroes will 'go away'.

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

It doesn't solve the '1.005 rounding problem' though - since it is intrinsic to how float fractions are being processed.

console.log(1.005 - 0.005);

If you are open to libraries you can use bignumber.js

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>
Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
  • 3
    `(1.005).toPrecision(3)` still returns `1.00` instead of `1.01` actually. – Giacomo Feb 23 '18 at 22:21
  • `toPrecision` returns a string which changes the desired output type. – adamduren Nov 16 '18 at 22:59
  • @Giacomo It is not a flaw of `.toPrecision` method, it is a specificity of floating-point numbers (which numbers in JS are) — try `1.005 - 0.005`, it will return `0.9999999999999999`. – shau-kote Nov 26 '18 at 01:44
  • 1
    `(1).toPrecision(3)` returns '1.00', but questioner wanted to have `1` in this case. – Eugene Mala Jun 07 '19 at 20:39
  • This also likely doesn't do what most people want with larger numbers without decimals - say 144. It will be rounded to 140. – Zach Saucier Jun 28 '19 at 17:19
  • 1
    As @Giacomo said, this answer seems to confuse "significant digits" with "rounding to a number of decimal places". `toPrecision` does the format, not the latter, and is not an answer to the OP's question, although it may seem at first relevant it gets a lot wrong. See https://en.wikipedia.org/wiki/Significant_figures. For example `Number(123.4).toPrecision(2)` returns `"1.2e+2"` and `Number(12.345).toPrecision(2)` returns `"12"`. I'd also agree with @adamduren's point that it returns a string which is not desirable (not a huge problem but not desirable). – Neek Jul 09 '19 at 04:02
  • Returns scientific notations, useless. – shinzou Jan 31 '20 at 08:58
26

The easiest approach would be to use toFixed and then strip trailing zeros using the Number function:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78
Marcin Wanago
  • 624
  • 7
  • 11
  • 3
    this does not work for all cases. do extensive tests before posting answers. – Neeraj Apr 19 '20 at 10:23
  • @baburao Please post a case in which the above solution doesn't work – Marcin Wanago Apr 20 '20 at 10:05
  • const number = 15; Number(number.toFixed(2)); //15.00 instead of 15 – Kevin Jhangiani Apr 23 '20 at 22:10
  • 2
    @KevinJhangiani const number = 15; Number(number.toFixed(2)); // 15 - I tested it both on newest Chrome and Firefox – Marcin Wanago Apr 24 '20 at 11:22
  • @KevinJhangiani how do you get `15.00`? Numbers in JS do *not* store the decimal places and any display automatically truncates excess decimal places (any zeroes at the end). – VLAZ May 18 '20 at 11:18
  • this doesn't work for any of the edge cases as discussed extensively throughout the answers in this thread. 1.005 becomes 1 when it should be 1.01. See my answer here for the most accurate method of rounding floating point values. – KFish Jul 16 '20 at 17:55
  • 1
    The commenters are totally right, and I realized the error in my code after posting that! – Kevin Jhangiani May 26 '21 at 16:30
  • This is a duplicate of some previous answers. For example, [this one](https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary/30253724#30253724). – Peter Mortensen May 05 '22 at 17:52
25

MarkG and Lavamantis offered a much better solution than the one that has been accepted. It's a shame they don't get more upvotes!

Here is the function I use to solve the floating point decimals issues also based on MDN. It is even more generic (but less concise) than Lavamantis's solution:

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));
}

Use it with:

round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

Compared to Lavamantis's solution, we can do...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
astorije
  • 2,666
  • 2
  • 27
  • 39
25

One way to achieve such a rounding only if necessary is to use Number.prototype.toLocaleString():

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

This will provide exactly the output you expect, but as strings. You can still convert those back to numbers if that's not the data type you expect.

justinsg
  • 738
  • 6
  • 11
Jérôme Beau
  • 10,608
  • 5
  • 48
  • 52
  • This is the cleanest solution there is by far and sidesteps all the complicated floating point issues, but [per MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString) support is still incomplete - Safari doesn't support passing arguments to `toLocaleString` yet. – Mark Amery Jul 30 '16 at 23:12
  • @MarkAmery For now, only Android Browser have some issues: https://caniuse.com/#search=toLocaleString – ptyskju Jan 10 '20 at 20:56
  • It's usable now. https://caniuse.com/mdn-javascript_builtins_number_tolocalestring – Martin Jun 13 '22 at 07:58
24

Keep the type as integer for later sorting or other arithmetic operations:

Math.round(1.7777777 * 100)/100

1.78

// Round up!
Math.ceil(1.7777777 * 100)/100

1.78

// Round down!
Math.floor(1.7777777 * 100)/100

1.77

Or convert to a string:

(1.7777777).toFixed(2)

"1.77"

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
profimedica
  • 2,716
  • 31
  • 41
22

It may work for you,

Math.round(num * 100)/100;

to know the difference between toFixed and round. You can have a look at Math.round(num) vs num.toFixed(0) and browser inconsistencies.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shreedhar
  • 5,502
  • 3
  • 22
  • 27
19

This is the simplest, more elegant solution (and I am the best of the world;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9

Modern syntax alternative with fallback values

const roundToX = (num = 0, X = 2) => +(Math.round(num + `e${X}`)  + `e-${X}`)

And the newest ES notation with power **:

const roundToX = (num = 0, decimals = 2) => Math.round(num * 10 ** decimals) / 10 ** decimals;
Soldeplata Saketos
  • 3,212
  • 1
  • 25
  • 38
  • 5
    That's a nice way to rewrite the accepted answer to accept an argument using `E` notation. – AxelH Nov 25 '16 at 08:48
  • 3
    This doesn't work in some edge cases: try ([jsfiddle](https://jsfiddle.net/x5odkv4o/)) `roundToX(362.42499999999995, 2)`. Expected result (as in PHP `echo round(362.42499999999995, 2)`): `362.43`. Actual result: `362.42` – Dr. Gianluigi Zane Zanettini Dec 06 '17 at 13:27
  • 10
    IMHO, your PHP result is wrong. No matter what comes after the third decimal, if the third decimal is lower than 5, then the second decimal should remain the same. That's the mathematical definition. – Soldeplata Saketos Dec 06 '17 at 20:08
  • 2
    To be even more concise "e+" can just be "e" instead. – Lonnie Best Jan 22 '19 at 06:58
18
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2): Here 2 is the number of digits up to which we want to round this number.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ritesh Dhuri
  • 215
  • 2
  • 7
  • this .toFixed() is more simple to implement. just go through it once. – Ritesh Dhuri May 15 '15 at 07:19
  • An explanation would be in order. E.g., what does this "Number" function do? Why is it required? What is the idea/gist? Some more input and output values would also be good, e.g. the previously mentioned `1.005`. From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/30253724/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen May 05 '22 at 16:41
18

See AmrAli's answer for a more thorough run through and performance breakdown of all the various adaptations of this solution.

var DecimalPrecision = (function(){
    if (Number.EPSILON === undefined) {
        Number.EPSILON = Math.pow(2, -52);
    }
    if(Number.isInteger === undefined){
        Number.isInteger = function(value) {
            return typeof value === 'number' &&
            isFinite(value) &&
            Math.floor(value) === value;
        };
    }
    this.isRound = function(n,p){
        let l = n.toString().split('.')[1].length;
        return (p >= l);
    }
    this.round = function(n, p=2){
        if(Number.isInteger(n) || this.isRound(n,p))
            return n;
        let r = 0.5 * Number.EPSILON * n;
        let o = 1; while(p-- > 0) o *= 10;
        if(n<0)
            o *= -1;
        return Math.round((n + r) * o) / o;
    }
    this.ceil = function(n, p=2){
        if(Number.isInteger(n) || this.isRound(n,p))
            return n;
        let r = 0.5 * Number.EPSILON * n;
        let o = 1; while(p-- > 0) o *= 10;

        return Math.ceil((n + r) * o) / o;
    }
    this.floor = function(n, p=2){
        if(Number.isInteger(n) || this.isRound(n,p))
            return n;
        let r = 0.5 * Number.EPSILON * n;
        let o = 1; while(p-- > 0) o *= 10;

        return Math.floor((n + r) * o) / o;
    }
    return this;
})();

console.log(DecimalPrecision.round(1.005));
console.log(DecimalPrecision.ceil(1.005));
console.log(DecimalPrecision.floor(1.005));
console.log(DecimalPrecision.round(1.0049999));
console.log(DecimalPrecision.ceil(1.0049999));
console.log(DecimalPrecision.floor(1.0049999));
console.log(DecimalPrecision.round(2.175495134384,7));
console.log(DecimalPrecision.round(2.1753543549,8));
console.log(DecimalPrecision.round(2.1755465135353,4));
console.log(DecimalPrecision.ceil(17,4));
console.log(DecimalPrecision.ceil(17.1,4));
console.log(DecimalPrecision.ceil(17.1,15));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
KFish
  • 440
  • 3
  • 9
  • 1
    (DecimalPrecision.round(0.014999999999999999, 2)) // returns 0.02 – Sergii May 12 '20 at 14:42
  • Good catch! The problem is with floating point storage in JS, there is always going to be some edge cases. The good news is that the math that you apply to Number.EPSILON first can be more finely tuned to push those edge cases further out on the edge. If you want to guarantee no possibility for edge cases your only real solution is going to be string manipulation, and then the math. The moment you perform any mathematical computation on the value (even in trying to move the decimal), then you have already produced the bug. – KFish May 14 '20 at 18:42
  • Actually, on further inspection, this is not due to any of the math involved, but rather the problem manifests immediately upon invoking the specified value. You can confirm this simply by typing that number into the console and see that it immediately evaluates to 0.015. Therefore, this would represent the absolute edge of accuracy for any floating point number in JS. In this case you couldn't even convert to string and manipulate as the string value would be "0.015" – KFish May 19 '20 at 01:06
  • Update the post with edge numbers: [1.005, 1.275, 2.675, 16.235] – Amr Ali Aug 01 '20 at 22:55
  • @AmrAli all of those numbers calculate correctly using this method. I'm not sure what you're asking. There are many edge cases that can be found that would cause the floating point rounding error with normal JS math functions. As far as I can tell, this method corrects for all of them, with the exception of numbers that couldn't even be stored by JS without automatically being reduced, such as the example that Sergey provided. You can test any number you like in the method I've provided, let us know if you find one that doesn't calculate accurately. – KFish Aug 02 '20 at 13:45
  • I'm the one who wrote the mathematical function for decimal rounding. There is no need to apply epsilon correction for ceil and floor functions. Just `Math.ceil(n * o) / o;` ** – Amr Ali Aug 02 '20 at 17:12
  • @AmrAli That is incorrect. The issue with floating point storage precision occurs the moment any mathematical operation is performed on the FP value, i.e. the multiplication inside of the Math.ceil() function. This means that for some edge cases the value will already be inaccurate before it is rounded up. For example: Math.ceil(2.1760000000000001 * 10000) / 10000 evaluates to 2.176 while DecimalPrecision.ceil(2.1760000000000001, 4) correctly evaluates to 2.1761 where the intent is to ceil (round up) to the next thousandth (4 decimals) – KFish Aug 10 '20 at 18:04
  • 1
    @KFish `DecimalPrecision.ceil(17,0); // 18` and `DecimalPrecision.ceil(17,1); // 17.1` – Amr Ali Aug 17 '20 at 19:56
  • @AmrAli, that's a good point. With the focus here being primarily on float values, I actually hadn't considered handling an integer input. The correct way to handle that would be to do nothing at all, as the correct result of applying the mathematical floor or ceil functions to a whole number is simply no change at all. I will update the answer to include an integer check and return the input value unchanged. – KFish Aug 19 '20 at 01:29
  • 1
    @KFish `DecimalPrecision.ceil(-5.12, 1); // -5.2` and `DecimalPrecision.floor(-5.12, 1); // -5.1` – Amr Ali Aug 20 '20 at 21:44
  • Thanks Amr, good catch. That was due to the inversion I was applying to the multiplier that shifts the decimal before applying the standard round/ceil/floor Math functions. I'm not really sure why I added that in the first place, in taking another look that inversion does not need to occur at all. – KFish Aug 22 '20 at 17:52
  • @KFish `DecimalPrecision.round(-0.5,0); // -0` either keep the sign inversion before Math.round() only, or use `0.51 * Number.EPSILON * n` before Math.round/ceil/floor functions. – Amr Ali Aug 22 '20 at 20:40
  • One more issue: `DecimalPrecision.ceil(0.0000001, 2)` gives me `Error: Cannot read property 'length' of undefined` – Amr Ali Aug 22 '20 at 20:56
  • Ah, you're right, the inversion does need to happen in round. That's why I had it in there originally, and just carried it over to ceil and floor. When I looked at it again and tested it wasn't apparent why I had used it even in round originally, but that's why. It also definitely doesn't handle exponential notation, so that's definitely an issue where JS automatically reduces to it. For that reason, I would say that your adaptation is the better approach. There's no sense in rewriting this answer to be the same, so I'll just leave it for posterity. – KFish Aug 23 '20 at 01:35
  • 1
    Regardless of any approach, it's important that people understand that there are always going to be some edge cases that don't evaluate correctly. For example in either your version or mine, this DecimalPrecision.ceil(10000000000000000.00111, 4) produces this result 10000000000000000, when it should technically be 10000000000000000.0012. Due to the handling of exponential notation, I would recommend your version over mine, but people need to realize that at best are only ever reducing the probability of error. – KFish Aug 23 '20 at 01:41
  • The number 10000000000000000.00111 is already above Number.MAX_SAFE_INTEGER. So, it is a limitation of the binary IEEE 754 format in the first place. For example `console.log(10000000000000000.00111); // 10000000000000000`. – Amr Ali Aug 23 '20 at 04:02
  • 1
    "Edit", "Update", etc. do not belong in this post. It ought to be changed to be as if it was written right now. The ***revision history*** retains the previous versions for ever. See e.g. *[Is it recommended to notify the answer "Edits" with an heading followed by the edit content?](https://meta.stackexchange.com/a/230693)*, *[When is "EDIT"/"UPDATE" appropriate in a post?](https://meta.stackexchange.com/a/127655)*, and *[Why are new editors discouraged & not given an opportunity to explain/defend?](https://meta.stackoverflow.com/a/378081)* – Peter Mortensen May 05 '22 at 17:30
17

Easiest way:

+num.toFixed(2)

It converts it to a string, and then back into an integer / float.

bigpotato
  • 26,262
  • 56
  • 178
  • 334
  • Thanks for this simplest answer. However, what is '+' in +num? It didn't work for me where the decimal val came in string. I did: (num * 1).toFixed(2). – Ethan Mar 22 '15 at 08:21
  • @momo just change the argument to `toFixed()` to 3. So it would be `+num.toFixed(3)`. That's working the way it's supposed to, 1.005 is rounded to 1.00, which is equal to 1 – bigpotato May 04 '15 at 16:13
  • 2
    @Edmund It's supposed to return 1.01, not 1.00 – mjs May 05 '15 at 19:20
  • This is a duplicate of [user3711536's answer](https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary/24406175#24406175)—though this one has some (if insufficinet) explanation. – Peter Mortensen May 05 '22 at 16:36
16

2022, native, without library, modern browser, clear and readable.

function round(
  value,
  minimumFractionDigits,
  maximumFractionDigits
) {
  const formattedValue = value.toLocaleString('en', {
    useGrouping: false,
    minimumFractionDigits,
    maximumFractionDigits
  })
  return Number(formattedValue)
}

console.log(round(21.891, 2, 3)) // 21.891
console.log(round(1.8, 2)) // 1.8, if you need 1.80, remove the `Number` function. Return directly the `formattedValue`.
console.log(round(21.0001, 0, 1)) // 21
console.log(round(0.875, 3)) // 0.875
Victor dlf
  • 276
  • 3
  • 7
  • 3
    Run this and see that results are not like you've written – Royi Namir Apr 09 '22 at 14:06
  • 3
    What is the gist of it? What is toLocaleString() supposed to do? Does it work for the number `1.015`? Why is Number() required? An explanation would be in order. From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/70879279/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen May 05 '22 at 18:10
15

Use something like this "parseFloat(parseFloat(value).toFixed(2))"

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1
Arulraj
  • 423
  • 3
  • 10
  • 1
    not if the inaccuracy is intrinsic to the float representation. you would just be removing it and then reintroducing the same error by converting back to float again! – Ben McIntyre Oct 28 '18 at 20:00
14

Here is a prototype method:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
arielf
  • 239
  • 2
  • 5
13

To not deal with many 0s, use this variant:

Math.round(num * 1e2) / 1e2
Daniel De León
  • 13,196
  • 5
  • 87
  • 72
  • What do you mean by *"To not deal with many 0s"*? Can you add an example to illustrate it? (But ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today.) – Peter Mortensen May 05 '22 at 15:55
  • Oh... "To not deal with many 0s" I wrote it just to emphasize the e-notation (elevation notation) B-) – Daniel De León May 07 '22 at 18:39
11

A different approach is to use a library. Use Lodash:

const _ = require("lodash")
const roundedNumber = _.round(originalNumber, 2)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Carlos Lombardi
  • 359
  • 2
  • 9
11

Instead of using Math.round as Brian Ustas suggests, I prefer the Math.trunc approach to fix the the following situation:

const twoDecimalRound = num => Math.round(num * 100) / 100;
const twoDecimalTrunc = num => Math.trunc(num * 100) / 100;
console.info(twoDecimalRound(79.996)); // Not desired output: 80;
console.info(twoDecimalTrunc(79.996)); // Desired output: 79.99;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nahuel Ramos
  • 121
  • 1
  • 3
10

If you happen to already be using the D3.js library, they have a powerful number formatting library.

Rounding specifically is at D3 round.

In your case, the answer is:

> d3.round(1.777777, 2)
1.78

> d3.round(1.7, 2)
1.7

> d3.round(1, 2)
1
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Scott Stafford
  • 43,764
  • 28
  • 129
  • 177
  • 2
    Looking at [the source](https://github.com/mbostock/d3/blob/master/src/format/round.js), this is nothing more than a generalized version of @ustasb answer, using `num * Math.pow(n)` instead of `num * 100` (but it's definitely a neat one-liner ;-) – svvac Jun 18 '14 at 14:23
  • But documented, and, being in a library, I don't have the same need to check browser compatibility BS. – Scott Stafford Jun 19 '14 at 18:11
  • using Math.pow(n) allows for d3.round(12, -1) == 10 – daviestar Aug 05 '14 at 09:25
10

A simpler ES6 way is

const round = (x, n) => 
  Number(parseFloat(Math.round(x * Math.pow(10, n)) / Math.pow(10, n)).toFixed(n));

This pattern also returns the precision asked for.

ex:

round(44.7826456, 4)  // yields 44.7826
round(78.12, 4)       // yields 78.12
Adam A.
  • 320
  • 3
  • 10
  • 1
    Unfortunately, your approach adds zeros at the end that are not necessary. I think the solution to the posted question should result in `78.12` instead of `78.1200`. – Marcin Wanago Apr 23 '20 at 16:57
  • @MarcinWanago good catch, I overlooked that in the OP's question. I've updated my example. Thank you. – Adam A. Apr 29 '20 at 02:06
9

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.

1.55 is the absolute correct result, because there exists no exact representation of 1.555 in the computer. If reading 1.555 it is rounded to the nearest possible value = 1.55499999999999994 (64 bit float). And rounding this number by toFixed(2) results in 1.55.

All other functions provided here give fault result, if the input is 1.55499999999999.

Solution: Append the digit "5" before scanning to rounding up (more exact: rounding away from 0) the number. Do this only, if the number is really a float (has a decimal point).

parseFloat("1.555"+"5").toFixed(2); // Returns 1.56
Wiimm
  • 2,971
  • 1
  • 15
  • 25
9

I reviewed every answer of this post. Here is my take on the matter:

const nbRounds = 7;
const round = (x, n=2) => {
  const precision = Math.pow(10, n)
  return Math.round((x+Number.EPSILON) * precision ) / precision;
}
let i = 0;
while( nbRounds > i++ ) {
  console.log("round(1.00083899, ",i,") > ", round(1.00083899, i))
  console.log("round(1.83999305, ",i,") > ", round(1.83999305, i))
}
Alexandre ELIOT
  • 146
  • 1
  • 9
  • 1
    Can you explain your take? What were the conclusions? Does it work for `1.015` (and other problematic numbers mentioned in comments to the other answers)? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/63176814/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen May 05 '22 at 17:59
8

A simple generic solution

const round = (n, dp) => {
  const h = +('1'.padEnd(dp + 1, '0')) // 10 or 100 or 1000 or etc
  return Math.round(n * h) / h
}

console.log('round(2.3454, 3)', round(2.3454, 3)) // 2.345
console.log('round(2.3456, 3)', round(2.3456, 3)) // 2.346
console.log('round(2.3456, 2)', round(2.3456, 2)) // 2.35

Or just use Lodash round which has the same signature - for example, _.round(2.3456, 2)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
danday74
  • 52,471
  • 49
  • 232
  • 283
7

A simple solution would be use Lodash's ceil function if you want to round up...

_.round(6.001, 2)

gives 6

_.ceil(6.001, 2);

gives 6.01

_.ceil(37.4929, 2);

gives 37.5

_.round(37.4929, 2);

gives 37.49

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pavithran Ravichandiran
  • 1,711
  • 1
  • 17
  • 20
  • 3
    There is no sense in importing new dependency, just to make rounding. – Dmytro Medvid Aug 02 '18 at 12:04
  • It is very funny that you don't use lodash already in your javascript project considering the kind of features lodash provides... – Pavithran Ravichandiran Aug 02 '18 at 13:33
  • 1
    which already has native implementation... I'm not saying that lodash (underscore) is useless, I'm just saying, that there is no sense to lodash in case you only need rounding. Of course if you like and use it on everyday basis and whole your project full of lodash calls - it is make sense. – Dmytro Medvid Aug 02 '18 at 14:37
7

Based on the chosen answer and the upvoted comment on the same question:

Math.round((num + 0.00001) * 100) / 100

This works for both these examples:

Math.round((1.005 + 0.00001) * 100) / 100

Math.round((1.0049 + 0.00001) * 100) / 100
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
6

Another approach to this:

number = 16.6666666;
console.log(parseFloat(number.toFixed(2)));
"16.67"

number = 16.6;
console.log(parseFloat(number.toFixed(2)));
"16.6"

number = 16;
console.log(parseFloat(number.toFixed(2)));
"16"

.toFixed(2) returns a string with exactly two decimal points, that may or may not be trailing zeros. Doing a parseFloat() will eliminate those trailing zeros.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marcos Lima
  • 761
  • 1
  • 10
  • 26
6

This did the trick for me (TypeScript):

round(decimal: number, decimalPoints: number): number{
    let roundedValue = Math.round(decimal * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints);

    console.log(`Rounded ${decimal} to ${roundedValue}`);
    return roundedValue;
}

Sample output

Rounded 18.339840000000436 to 18.34
Rounded 52.48283999999984 to 52.48
Rounded 57.24612000000036 to 57.25
Rounded 23.068320000000142 to 23.07
Rounded 7.792980000000398 to 7.79
Rounded 31.54157999999981 to 31.54
Rounded 36.79686000000004 to 36.8
Rounded 34.723080000000124 to 34.72
Rounded 8.4375 to 8.44
Rounded 15.666960000000074 to 15.67
Rounded 29.531279999999924 to 29.53
Rounded 8.277420000000006 to 8.28
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Web Dev
  • 2,677
  • 2
  • 30
  • 41
6

The question is to round to two decimals.

Let’s not make this complicated, modifying prototype chain, etc.

Here is one-line solution

let round2dec = num => Math.round(num * 100) / 100;

console.log(round2dec(1.77));
console.log(round2dec(1.774));
console.log(round2dec(1.777));
console.log(round2dec(10));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Salman
  • 892
  • 12
  • 13
6

The mathematical floor and round definitions:

Enter image description here

lead us to

let round= x=> ( x+0.005 - (x+0.005)%0.01 +'' ).replace(/(\...)(.*)/,'$1');

// for a case like 1.384 we need to use a regexp to get only 2 digits after the dot
// and cut off machine-error (epsilon)

console.log(round(10));
console.log(round(1.7777777));
console.log(round(1.7747777));
console.log(round(1.384));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
6

A simple general rounding function could be following:

Steps are:

  1. Multiply the number by (10 to the power of number of decimal place) using Math.pow(10,places).
  2. Round the result to whole integer using Math.Round.
  3. Divide the result back by (10 to the power of number of decimal place) Math.pow(10,places).

Example:

number is: 1.2375 to be rounded to 3 decimal places

  1. 1.2375 * (10^3) ==> 1.2375 * 1000 = 1237.5
  2. Round to integer ==> 1238
  3. Divide 1238 by (10^3) ==> 1238 / 1000 = 1.238

(note: 10^3 means Math.pow(10,3)).

 function numberRoundDecimal(v,n) {
 return Math.round((v+Number.EPSILON)*Math.pow(10,n))/Math.pow(10,n)}


// ------- tests --------
console.log(numberRoundDecimal(-0.024641163062896567,3))  // -0.025
console.log(numberRoundDecimal(0.9993360575508052,3))     // 0.999
console.log(numberRoundDecimal(1.0020739645577939,3))     // 1.002
console.log(numberRoundDecimal(0.975,0))                  // 1
console.log(numberRoundDecimal(0.975,1))                  // 1
console.log(numberRoundDecimal(0.975,2))                  // 0.98
console.log(numberRoundDecimal(1.005,2))                  // 1.01
penduDev
  • 4,743
  • 35
  • 37
Mohsen Alyafei
  • 4,765
  • 3
  • 30
  • 42
6

I've read all the answers, the answers of similar questions and the complexity of the most "good" solutions didn't satisfy me. I don't want to put a huge round function set, or a small one but fails on scientific notation. So, I came up with this function. It may help someone in my situation:

function round(num, dec) {
   const [sv, ev] = num.toString().split('e');
   return Number(Number(Math.round(parseFloat(sv + 'e' + dec)) + 'e-' + dec) + 'e' + (ev || 0));
}

I didn't run any performance test because I will call this just to update the UI of my application. The function gives the following results for a quick test:

// 1/3563143 = 2.806510993243886e-7
round(1/3563143, 2)  // returns `2.81e-7`

round(1.31645, 4)    // returns 1.3165

round(-17.3954, 2)   // returns -17.4

This is enough for me.

superkeci
  • 151
  • 2
  • 3
5

To round at decimal positions pos (including no decimals) do Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos)

var console = {
 log: function(s) {
  document.getElementById("console").innerHTML += s + "<br/>"
 }
}
var roundDecimals=function(num,pos) {
 return (Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos) );
}
//https://en.wikipedia.org/wiki/Pi
var pi=3.14159265358979323846264338327950288419716939937510;
for(var i=2;i<15;i++) console.log("pi="+roundDecimals(pi,i));
for(var i=15;i>=0;--i) console.log("pi="+roundDecimals(pi,i));
<div id="console" />
loretoparisi
  • 15,724
  • 11
  • 102
  • 146
5

I know there are many answers, but most of them have side effect in some specific cases.

Easiest and shortest solution without any side effects is following:

Number((2.3456789).toFixed(2)) // 2.35

It rounds properly and returns number instead of string

console.log(Number((2.345).toFixed(2)))  // 2.35
console.log(Number((2.344).toFixed(2)))  // 2.34
console.log(Number((2).toFixed(2)))      // 2
console.log(Number((-2).toFixed(2)))     // -2
console.log(Number((-2.345).toFixed(2))) // -2.35

console.log(Number((2.345678).toFixed(3))) // 2.346
Nicolo
  • 1,600
  • 2
  • 16
  • 18
5

This function works for me. You just pass in the number and the places you want to round and it does what it needs to do easily.

round(source, n) {
  let places = Math.pow(10, n);

  return Math.round(source * places) / places;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jerry
  • 141
  • 2
  • 4
  • This approach works well, thanks. (Note: doing the opposite does *not* work well, e.g.: `Math.round(5.3473483447 / 0.000001) * 0.000001 == 5.347347999999999`). – Andrew May 14 '20 at 02:51
  • It may work for you, but does it answer the question? – Peter Mortensen May 05 '22 at 17:56
  • 1
    @Alex Why do you expect 4.45 to round up to 5? It's less than 4.5. – rrauenza Sep 09 '22 at 20:27
  • 1
    @rrauenza you are right.. I don't know why I thought that it was not a correct answer :) Looks right to me. Comment removed. – Alex Oct 18 '22 at 18:00
4

Try to use the jQuery .number plug-in:

var number = 19.8000000007;
var res = 1 * $.number(number, 2);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
4

You could also override the Math.round function to do the rounding correct and add a parameter for decimals and use it like: Math.round(Number, Decimals). Keep in mind that this overrides the built in component Math.round and giving it another property then it original is.

var round = Math.round;
Math.round = function (value, decimals) {
  decimals = decimals || 0;
  return Number(round(value + 'e' + decimals) + 'e-' + decimals);
}

Then you can simply use it like this:

Math.round(1.005, 2);

https://jsfiddle.net/k5tpq3pd/3/

Arne H. Bitubekk
  • 2,963
  • 1
  • 27
  • 34
4

I was building a simple tipCalculator and there was a lot of answers here that seemed to overcomplicate the issue. So I found summarizing the issue to be the best way to truly answer this question.

If you want to create a rounded decimal number, first you call toFixed(# of decimal places you want to keep) and then wrap that in a Number().

So the end result:

let amountDue = 286.44;
tip = Number((amountDue * 0.2).toFixed(2));
console.log(tip)  // 57.29 instead of 57.288
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Denis S Dujota
  • 543
  • 5
  • 13
  • This does not answer the question, which was about rounding and getting back a number. `toFixed` simply truncates the value and returns a string. `Number(1.005).toFixed(2) => "1.00"` – brainbag Jul 17 '18 at 22:10
  • And this solution also doesn't go with the "only if necessary" part of the question: `Number(10).toFixed(2) => "10.00"` – GriffoGoes Oct 24 '18 at 19:36
4

The rounding problem can be avoided by using numbers represented in exponential notation.

public roundFinancial(amount: number, decimals: number) {
    return Number(Math.round(Number(`${amount}e${decimals}`)) + `e-${decimals}`);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dmytro Medvid
  • 4,988
  • 3
  • 25
  • 29
  • 1
    this doesn't look like javascript to me, and chrome doesn't accept it - `VM82:1 Uncaught SyntaxError: Unexpected identifier` – hanshenrik Oct 16 '18 at 16:03
  • This is TypeScript, but after changing to `function round(amount, decimals) { return Number(Math.round(Number(\`${amount}e${decimals}\`)) + \`e-${decimals}\`); }` it seems to do the job well. – Adam Jagosz Aug 28 '19 at 16:19
3

Here is a function I came up with to do "round up". I used double Math.round to compensate for JavaScript's inaccurate multiplying, so 1.005 will be correctly rounded as 1.01.

function myRound(number, decimalplaces){
    if(decimalplaces > 0){
        var multiply1 = Math.pow(10,(decimalplaces + 4));
        var divide1 = Math.pow(10, decimalplaces);
        return Math.round(Math.round(number * multiply1)/10000 )/divide1;
    }
    if(decimalplaces < 0){
        var divide2 = Math.pow(10, Math.abs(decimalplaces));
        var multiply2 = Math.pow(10, Math.abs(decimalplaces));
        return Math.round(Math.round(number / divide2) * multiply2);
    }
    return Math.round(number);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrei
  • 882
  • 8
  • 13
  • 1
    I tested it again, and it works for me.. (alert( myRound(1234.56789, 2) ); //1234.57 ) May-be you get confused by multiple "return" statements? – Andrei Sep 19 '13 at 06:18
  • An upvote as this maybe a long winded way of doing it but it works. – Ashutosh May 26 '14 at 10:52
3

I wrote the following set of functions for myself. Maybe it will help you too.

function float_exponent(number) {
    exponent = 1;
    while (number < 1.0) {
        exponent += 1
        number *= 10
    }
    return exponent;
}
function format_float(number, extra_precision) {
    precision = float_exponent(number) + (extra_precision || 0)
    return number.toFixed(precision).split(/\.?0+$/)[0]
}

Usage:

format_float(1.01); // 1
format_float(1.06); // 1.1
format_float(0.126); // 0.13
format_float(0.000189); // 0.00019

For you case:

format_float(10, 1); // 10
format_float(9.1, 1); // 9.1
format_float(1.77777, 1); // 1.78
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vsvasya
  • 657
  • 6
  • 11
3

Here is the shortest and complete answer:

function round(num, decimals) {
        var n = Math.pow(10, decimals);
        return Math.round( (n * num).toFixed(decimals) )  / n;
};

This also takes care of the example case 1.005 which will return 1.01.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mjs
  • 21,431
  • 31
  • 118
  • 200
  • 1
    Still fails for some number. Try round(1234.00000254495, 10) where it will return 1234.0000025449. The method by Mozilla correctly, which you commented on, properly rounds that value. – LostInComputer May 11 '15 at 10:59
  • @LostInComputer (10.00000254495).toFixed(10) returns "10.0000025450", (100.00000254495).toFixed(10) returns "100.0000025449", and guess what (100.00000254495).toFixed(10) returns? Yes: "1000.0000025450". So the problem is more of other reasons than this method. Try this: console.log(1234578.9012345689) ... If you move the the dot right, then the number is going to grow and you won't get as many decimals. You can't get more than 18 numbers total ( in this example ). Then try this: console.log(123455.00000254495), it will output 123455.00000254496. That's how the browsers handle numbers. – mjs May 11 '15 at 12:37
  • 3
    Sorry, wrong explanation. Plus, [Mozilla's implementation - at the bottom of the page and also posted here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), can properly round that number. TLDR: Mozilla passes my test case. Yours don't – LostInComputer May 12 '15 at 02:09
3

Starting from the example proposed over the precisionRound that I found on MDN (that event for 1.005 returns 1 and not 1.01), I write a custom precisionRound that manage a random precision number and for 1.005 returns 1.01.

This is the function:

function precisionRound(number, precision)
{
  if(precision < 0)
  {
    var factor = Math.pow(10, precision);
    return Math.round(number * factor) / factor;
  }
  else
    return +(Math.round(number + "e+"+precision)  + "e-"+precision);
}

console.log(precisionRound(1234.5678, 1));  // output: 1234.6
console.log(precisionRound(1234.5678, -1)); // output: 1230
console.log(precisionRound(1.005, 2));      // output: 1.01
console.log(precisionRound(1.0005, 2));     // output: 1
console.log(precisionRound(1.0005, 3));     // output: 1.001
console.log(precisionRound(1.0005, 4));     // output: 1.0005

For TypeScript:

public static precisionRound(number: number, precision: number)
{
  if (precision < 0)
  {
    let factor = Math.pow(10, precision);
    return Math.round(number * factor) / factor;
  }
  else
    return +(Math.round(Number(number + "e+" + precision)) +
      "e-" + precision);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marco Barbero
  • 1,460
  • 1
  • 12
  • 22
  • I would like to know why it was given a downvote? Who does it should also comment on the reason. This function is useful and is not among those already present among the answers. – Marco Barbero Feb 21 '18 at 07:10
3

The big challenge on this seemingly simple task is that we want it to yield psychologically expected results even if the input contains minimal rounding errors to start with (not mentioning the errors which will happen within our calculation). If we know that the real result is exactly 1.005, we expect that rounding to two digits yields 1.01, even if the 1.005 is the result of a large computation with loads of rounding errors on the way.

The problem becomes even more obvious when dealing with floor() instead of round(). For example, when cutting everything away after the last two digits behind the dot of 33.3, we would certainly not expect to get 33.29 as a result, but that is what happens:

console.log(Math.floor(33.3 * 100) / 100)

In simple cases, the solution is to perform calculation on strings instead of floating point numbers, and thus avoid rounding errors completely. However, this option fails at the first non-trivial mathematical operation (including most divsions), and it is slow.

When operating on floating point numbers, the solution is to introduce a parameter which names the amount by which we are willing to deviate from the actual computation result, in order to output the psychologically expected result.

var round = function(num, digits = 2, compensateErrors = 2) {
  if (num < 0) {
    return -this.round(-num, digits, compensateErrors);
  }
  const pow = Math.pow(10, digits);
  return (Math.round(num * pow * (1 + compensateErrors * Number.EPSILON)) / pow);
}

/* --- testing --- */

console.log("Edge cases mentioned in this thread:")
var values = [ 0.015, 1.005, 5.555, 156893.145, 362.42499999999995, 1.275, 1.27499, 1.2345678e+2, 2.175, 5.015, 58.9 * 0.15 ];
values.forEach((n) => {
  console.log(n + " -> " + round(n));
  console.log(-n + " -> " + round(-n));
});

console.log("\nFor numbers which are so large that rounding cannot be performed anyway within computation precision, only string-based computation can help.")
console.log("Standard: " + round(1e+19));
console.log("Compensation = 1: " + round(1e+19, 2, 1));
console.log("Effectively no compensation: " + round(1e+19, 2, 0.4));

Note: Internet Explorer does not know Number.EPSILON. If you are in the unhappy position of still having to support it, you can use a shim, or just define the constant yourself for that specific browser family.

malamut
  • 456
  • 4
  • 12
3

A slight variation on this is if you need to format a currency amount as either being a whole amount of currency or an amount with fractional currency parts.

For example:

1 should output $1

1.1 should output $1.10

1.01 should output $1.01

Assuming amount is a number:

const formatAmount = (amount) => amount % 1 === 0 ? amount : amount.toFixed(2);

If amount is not a number then use parseFloat(amount) to convert it to a number.

Ibraheem
  • 2,168
  • 20
  • 27
3

A slight modification of this answer that seems to work well.

Function

function roundToStep(value, stepParam) {
   var step = stepParam || 1.0;
   var inv = 1.0 / step;
   return Math.round(value * inv) / inv;
}

Usage

roundToStep(2.55) = 3
roundToStep(2.55, 0.1) = 2.6
roundToStep(2.55, 0.01) = 2.55
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Crashalot
  • 33,605
  • 61
  • 269
  • 439
3

As per the answer already given in comments with the link to http://jsfiddle.net/AsRqx/, the following one worked perfectly for me.

function C(num)
{
    return +(Math.round(num + "e+2") + "e-2");
}

function N(num, places)
{
    return +(Math.round(num + "e+" + places) + "e-" + places);
}

C(1.005);

N(1.005, 0);
N(1.005, 1); // Up to 1 decimal places
N(1.005, 2); // Up to 2 decimal places
N(1.005, 3); // Up to 3 decimal places
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ramya
  • 2,350
  • 6
  • 31
  • 57
3

This works correctly with positive, negative and large numbers:

function Round(value) {
    const neat = +(Math.abs(value).toPrecision(15));
    const rounded = Math.round(neat * 100) / 100;

    return rounded * Math.sign(value);
}

//0.244 -> 0.24
//0.245 -> 0.25
//0.246 -> 0.25

//-0.244 -> -0.24
//-0.245 -> -0.25
//-0.246 -> -0.25
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Buzzy
  • 1,783
  • 18
  • 15
3

A function with readable options is much more intuitive:

function round_number(options) {
    const places = 10**options.decimal_places;
    const res = Math.round(options.number * places)/places;
    return(res)
}

Usage:

round_number({
    number : 0.5555555555555556,
    decimal_places : 3
})

0.556
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cybernetic
  • 12,628
  • 16
  • 93
  • 132
  • Is there a name for this technique? Parameter destructuring? – Peter Mortensen Oct 19 '22 at 02:09
  • @PeterMortensen it's simply called "using objects as arguments." A single object as an argument is much cleaner than multiple arguments, and, IMO, a more natural way to work with JavaScript. – Cybernetic Oct 19 '22 at 02:56
2

Just for the record, the scaling method could theoretically return Infinity if the number and the digits you want to round to are big enough. In JavaScript that shouldn't be a problem since the maximum number is 1.7976931348623157e+308, but if you're working with really big numbers or a lot of decimal places you could try this function instead:

Number.prototype.roundTo = function(digits)
{
    var str = this.toString();
    var split = this.toString().split('e');
    var scientific = split.length > 1;
    var index;
    if (scientific)
    {
        str = split[0];
        var decimal = str.split('.');
        if (decimal.length < 2)
            return this;
        index = decimal[0].length + 1 + digits;
    }
    else
        index = Math.floor(this).toString().length + 1 + digits;
    if (str.length <= index)
        return this;
    var digit = str[index + 1];
    var num = Number.parseFloat(str.substring(0, index));
    if (digit >= 5)
    {
        var extra = Math.pow(10, -digits);
        return this < 0 ? num - extra : num + extra;
    }
    if (scientific)
        num += "e" + split[1];
    return num;
}
Martin
  • 335
  • 4
  • 13
2

From the existing answers I found another solution which seems to work great, which also works with sending in a string and eliminates trailing zeros.

function roundToDecimal(string, decimals) {
    return parseFloat(parseFloat(string).toFixed(decimals));
}

It doesn't take in to account if you send in some bull.. like "apa" though. Or it will probably throw an error which I think is the proper way anyway, it's never good to hide errors that should be fixed (by the calling function).

OZZIE
  • 6,609
  • 7
  • 55
  • 59
2

This worked pretty well for me when wanting to always round up to a certain decimal. The key here is that we will always be rounding up with the Math.ceil function.

You could conditionally select ceil or floor if needed.

     /**
     * Possibility to lose precision at large numbers
     * @param number
     * @returns Number number
     */
    var roundUpToNearestHundredth = function(number) {

        // Ensure that we use high precision Number
        number = Number(number);

        // Save the original number so when we extract the Hundredth decimal place we don't bit switch or lose precision
        var numberSave = Number(number.toFixed(0));

        // Remove the "integer" values off the top of the number
        number = number - numberSave;

        // Get the Hundredth decimal places
        number *= 100;

        // Ceil the decimals.  Therefore .15000001 will equal .151, etc.
        number = Math.ceil(number);

        // Put the decimals back into their correct spot
        number /= 100;

        // Add the "integer" back onto the number
        return number + numberSave;

    };

console.log(roundUpToNearestHundredth(6132423.1200000000001))
2

There is a solution working for all numbers. Give it a try. The expression is given below.

Math.round((num + 0.00001) * 100) / 100. 

Try Below Ex:

Math.round((1.005 + 0.00001) * 100) / 100 
Math.round((1.0049 + 0.00001) * 100) / 100

I recently tested every possible solution and finally arrived at the output after trying almost 10 times.

Here is a screenshot of issue arose during calculations,

Screen capture.

Head over to the amount field. It's returning almost infinite. I gave it a try for the toFixed() method, but it's not working for some cases (i.e., try with pi) and finally derived a solution given above.

Kishan Donga
  • 2,851
  • 2
  • 23
  • 35
Kiran Maniya
  • 8,453
  • 9
  • 58
  • 81
1

I just wanted to share my approach, based on previously mentioned answers:

Let's create a function that rounds any given numeric value to a given amount of decimal places:

function roundWDecimals(n, decimals) {
    if (!isNaN(parseFloat(n)) && isFinite(n)) {
        if (typeof(decimals) == typeof(undefined)) {
            decimals = 0;
        }
        var decimalPower = Math.pow(10, decimals);
        return Math.round(parseFloat(n) * decimalPower) / decimalPower;
    }
    return NaN;
}

And introduce a new "round" method for numbers prototype:

Object.defineProperty(Number.prototype, 'round', {
    enumerable: false,
    value: function(decimals) {
        return roundWDecimals(this, decimals);
    }
});

And you can test it:

function roundWDecimals(n, decimals) {
    if (!isNaN(parseFloat(n)) && isFinite(n)) {
        if (typeof(decimals) == typeof(undefined)) {
            decimals = 0;
        }
        var decimalPower = Math.pow(10, decimals);
        return Math.round(parseFloat(n) * decimalPower) / decimalPower;
    }
    return NaN;
}
Object.defineProperty(Number.prototype, 'round', {
    enumerable: false,
    value: function(decimals) {
        return roundWDecimals(this, decimals);
    }
});

var roundables = [
    {num: 10, decimals: 2},
    {num: 1.7777777, decimals: 2},
    {num: 9.1, decimals: 2},
    {num: 55.55, decimals: 1},
    {num: 55.549, decimals: 1},
    {num: 55, decimals: 0},
    {num: 54.9, decimals: 0},
    {num: -55.55, decimals: 1},
    {num: -55.551, decimals: 1},
    {num: -55, decimals: 0},
    {num: 1.005, decimals: 2},
    {num: 1.005, decimals: 2},
    {num: 19.8000000007, decimals: 2},
  ],
  table = '<table border="1"><tr><th>Num</th><th>Decimals</th><th>Result</th></tr>';
$.each(roundables, function() {
  table +=
    '<tr>'+
      '<td>'+this.num+'</td>'+
      '<td>'+this.decimals+'</td>'+
      '<td>'+this.num.round(this.decimals)+'</td>'+
    '</tr>'
  ;
});
table += '</table>';
$('.results').append(table);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="results"></div>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Deele
  • 3,728
  • 2
  • 33
  • 51
1

A generic answer for all browsers and precisions:

function round(num, places) {
    if(!places) {
        return Math.round(num);
    }

    var val = Math.pow(10, places);
    return Math.round(num * val) / val;
}

round(num, 2);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RBZ
  • 2,034
  • 17
  • 34
1

In the Node.js environment I just use the roundTo module:

const roundTo = require('round-to');
...
roundTo(123.4567, 2);

// 123.46
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Simon Prickett
  • 3,838
  • 1
  • 13
  • 26
1

This answer is more about speed.

var precalculatedPrecisions = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10];

function round(num, _prec) {
    _precision = precalculatedPrecisions[_prec]
    return Math.round(num * _precision + 1e-14) / _precision ;
}

jsPerf about this.

aleha_84
  • 8,309
  • 2
  • 38
  • 46
1

Here's my solution to this problem:

function roundNumber(number, precision = 0) {
    var num = number.toString().replace(",", "");
    var integer, decimal, significantDigit;

    if (num.indexOf(".") > 0 && num.substring(num.indexOf(".") + 1).length > precision && precision > 0) {
        integer = parseInt(num).toString();
        decimal = num.substring(num.indexOf(".") + 1);
        significantDigit = Number(decimal.substr(precision, 1));

        if (significantDigit >= 5) {
            decimal = (Number(decimal.substr(0, precision)) + 1).toString();
            return integer + "." + decimal;
        } else {
            decimal = (Number(decimal.substr(0, precision)) + 1).toString();
            return integer + "." + decimal;
        }
    }
    else if (num.indexOf(".") > 0) {
        integer = parseInt(num).toString();
        decimal = num.substring(num.indexOf(".") + 1);
        significantDigit = num.substring(num.length - 1, 1);

        if (significantDigit >= 5) {
            decimal = (Number(decimal) + 1).toString();
            return integer + "." + decimal;
        } else {
            return integer + "." + decimal;
        }
    }

    return number;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

I have found this works for all my use cases:

const round = (value, decimalPlaces = 0) => {
    const multiplier = Math.pow(10, decimalPlaces);
    return Math.round(value * multiplier + Number.EPSILON) / multiplier;
};

Keep in mind that is ES6. An ES5 equivalent would be very easy to code though, so I'm not is going to add it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rob Evans
  • 6,750
  • 4
  • 39
  • 56
  • if you have a value like `15.7949999999...` and use your function to round to 2 comma values you get a value of `15.79` which is wrong as the 9s from behind should lead to the 4 changing to a 5 and thus forcing the leading .79 decimals to change to a .80 instead, which should end up in a final result of `15.8` or `15.80` – Roman Vottner Jul 08 '20 at 12:29
  • @RomanVottner Thanks Roman, that does indeed indicate a flaw. – Rob Evans Jul 09 '20 at 13:15
1

A helper function where rounging is your default rounding:

let rounding = 4;

let round = (number) => { let multiply = Math.pow(10,rounding);  return Math.round(number*multiply)/multiply};

console.log(round(0.040579431));

=> 0.0406

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

The proposed answers, while generally correct, don't consider the precision of the passed in number, which is not expressed as requirement in the original question, but it may be a requirement in case of scientific application where 3 is different from 3.00 (for example) as the number of decimal digits represents the precision of the instrument that have acquired the value or the accuracy of a calculation.

In fact, the proposed answers rounds 3.001 to 3 while by keeping the information about the precision of the number should be 3.00.

Below is a function that takes that in account:

function roundTo(value, decimal) {

    let absValue = Math.abs(value);
    let int = Math.floor(absValue).toString().length;
    let dec = absValue.toString().length - int;
    dec -= (Number.isInteger(absValue) ? 0 : 1);
    return value.toPrecision(int + Math.min(dec, decimal));

}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mosè Bottacini
  • 4,016
  • 2
  • 24
  • 28
1

Here's a modified version of astorije's answer that better supports rounding negative values.

// https://stackoverflow.com/a/21323513/384884
// Modified answer from astorije
function round(value, precision) {
    // Ensure precision exists
    if (typeof precision === "undefined" || +precision === 0) {
        // Just do a regular Math.round
        return Math.round(value);
    }

    // Convert the value and precision variables both to numbers
    value = +value;
    precision = +precision;

    // Ensure the value is a number and that precision is usable
    if (isNaN(value) || !(typeof precision === "number" && precision % 1 === 0)) {
        // Return NaN
        return NaN;
    }

    // Get the sign of value
    var signValue = Math.sign(value);

    // Get the absolute value of value
    value = Math.abs(value);

    // Shift
    value = value.toString().split("e");
    value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] + precision) : precision)));

    // Shift back
    value = value.toString().split("e");
    value = +(value[0] + "e" + (value[1] ? (+value[1] - precision) : -precision));

    // Apply the sign
    value = value * signValue;

    // Return rounded value
    return value;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Deejers
  • 3,087
  • 2
  • 23
  • 20
1

My solution considers the input as a string and uses the algorithm of "mathematical rounding" to n digits: take n digits, and add one if digit n+1 is 5 or more. It also allows specifying negative digits, for example rounding 123.45 to -1 digits is 120. It works with scientific notation (e.g. 1.2e-3), as well. I did not measure its speed and I don't think it was the best performance-wise.

function safeRound( numInput, numPrecision ) {
    const strNumber = numInput.toString().replace( 'E', 'e' );
    const bSign = '+-'.indexOf( strNumber[ 0 ] ) !== -1;
    const strSign = bSign  ?  strNumber[ 0 ]  :  '';
    const numSign = strSign !== '-'  ?  +1  :  -1;
    const ixExponent = ( ixFound => ixFound !== -1  ?  ixFound  :  strNumber.length )( strNumber.indexOf( 'e' ) );
    const strExponent = strNumber.substr( ixExponent + 1 );
    const numExponent = ixExponent !== strNumber.length  ?  Number.parseInt( strExponent )  :  0;
    const ixDecimal = ( ixFound => ixFound !== -1  ?  ixFound  :  ixExponent )( strNumber.indexOf( '.' ) );
    const strInteger = strNumber.substring( !bSign  ?  0  :  1, ixDecimal );
    const strFraction = strNumber.substring( ixDecimal + 1, ixExponent );
    
    const numPrecisionAdjusted = numPrecision + numExponent;
    const strIntegerKeep = strInteger.substring( 0, strInteger.length + Math.min( 0, numPrecisionAdjusted ) ) + '0'.repeat( -Math.min( 0, numPrecisionAdjusted ) );
    const strFractionKeep = strFraction.substring( 0, Math.max( 0, numPrecisionAdjusted ) );
    const strRoundedDown = strSign + ( strIntegerKeep === ''  ?  '0'  :  strIntegerKeep ) + ( strFractionKeep === ''  ?  ''  :  '.' + strFractionKeep ) + ( strExponent === ''  ?  ''  :  'e' + strExponent );
    
    const chRoundUp = 0 <= numPrecisionAdjusted  ?  strFraction.substr( numPrecisionAdjusted, 1 )  :  ( '0' + strInteger ).substr( numPrecisionAdjusted, 1 );
    const bRoundUp = '5' <= chRoundUp && chRoundUp <= '9';
    const numRoundUp = bRoundUp  ?  numSign * Math.pow( 10, -numPrecision )  :  0;
    
    return Number.parseFloat( strRoundedDown ) + numRoundUp;
}

function safeRoundTest( numInput, numPrecision, strExpected ) {
    const strActual = safeRound( numInput, numPrecision ).toString();
    const bPassed = strActual === strExpected;
    console.log( 'numInput', numInput, 'numPrecision', numPrecision, 'strExpected', strExpected, 'strActual', strActual, 'bPassed', bPassed );
    return bPassed  ?  0  :  1;
}

function safeRoundTests() {
    let numFailed = 0;
    numFailed += safeRoundTest( 0, 0, '0' );
    numFailed += safeRoundTest( '0', 0, '0' );
    numFailed += safeRoundTest( '0.1', 0, '0' );
    numFailed += safeRoundTest( '+0.1', 0, '0' );
    numFailed += safeRoundTest( '-0.1', 0, '0' );
    numFailed += safeRoundTest( '0.1', 1, '0.1' );
    numFailed += safeRoundTest( '+0.1', 1, '0.1' );
    numFailed += safeRoundTest( '-0.1', 1, '-0.1' );
    numFailed += safeRoundTest( '0.9', 0, '1' );
    numFailed += safeRoundTest( '+0.9', 0, '1' );
    numFailed += safeRoundTest( '-0.9', 0, '-1' );
    numFailed += safeRoundTest( '0.9', 1, '0.9' );
    numFailed += safeRoundTest( '+0.9', 1, '0.9' );
    numFailed += safeRoundTest( '-0.9', 1, '-0.9' );
    numFailed += safeRoundTest( '0.5', 0, '1' );
    numFailed += safeRoundTest( '+0.5', 0, '1' );
    numFailed += safeRoundTest( '-0.5', 0, '-1' );
    numFailed += safeRoundTest( '0.4999', 0, '0' );
    numFailed += safeRoundTest( '+0.4999', 0, '0' );
    numFailed += safeRoundTest( '-0.4999', 0, '0' );
    numFailed += safeRoundTest( '1.005', 2, '1.01' );
    numFailed += safeRoundTest( '1.00499999999', 2, '1' );
    numFailed += safeRoundTest( '012.3456', -4, '0' );
    numFailed += safeRoundTest( '012.3456', -3, '0' );
    numFailed += safeRoundTest( '012.3456', -2, '0' );
    numFailed += safeRoundTest( '012.3456', -1, '10' );
    numFailed += safeRoundTest( '012.3456', 0, '12' );
    numFailed += safeRoundTest( '012.3456', 1, '12.3' );
    numFailed += safeRoundTest( '012.3456', 2, '12.35' );
    numFailed += safeRoundTest( '012.3456', 3, '12.346' );
    numFailed += safeRoundTest( '012.3456', 4, '12.3456' );
    numFailed += safeRoundTest( '012.3456', 5, '12.3456' );
    numFailed += safeRoundTest( '12.', 0, '12' );
    numFailed += safeRoundTest( '.12', 2, '0.12' );
    numFailed += safeRoundTest( '0e0', 0, '0' );
    numFailed += safeRoundTest( '1.2e3', 0, '1200' );
    numFailed += safeRoundTest( '1.2e+3', 0, '1200' );
    numFailed += safeRoundTest( '1.2e-3', 0, '0' );
    numFailed += safeRoundTest( '1.2e-3', 3, '0.001' );
    numFailed += safeRoundTest( '1.2e-3', 4, '0.0012' );
    numFailed += safeRoundTest( '1.2e-3', 5, '0.0012' );
    numFailed += safeRoundTest( '+12.', 0, '12' );
    numFailed += safeRoundTest( '+.12', 2, '0.12' );
    numFailed += safeRoundTest( '+0e0', 0, '0' );
    numFailed += safeRoundTest( '+1.2e3', 0, '1200' );
    numFailed += safeRoundTest( '+1.2e+3', 0, '1200' );
    numFailed += safeRoundTest( '+1.2e-3', 0, '0' );
    numFailed += safeRoundTest( '+1.2e-3', 3, '0.001' );
    numFailed += safeRoundTest( '+1.2e-3', 4, '0.0012' );
    numFailed += safeRoundTest( '+1.2e-3', 5, '0.0012' );
    numFailed += safeRoundTest( '-12.', 0, '-12' );
    numFailed += safeRoundTest( '-.12', 2, '-0.12' );
    numFailed += safeRoundTest( '-0e0', 0, '0' );
    numFailed += safeRoundTest( '-1.2e3', 0, '-1200' );
    numFailed += safeRoundTest( '-1.2e+3', 0, '-1200' );
    numFailed += safeRoundTest( '-1.2e-3', 0, '0' );
    numFailed += safeRoundTest( '-1.2e-3', 3, '-0.001' );
    numFailed += safeRoundTest( '-1.2e-3', 4, '-0.0012' );
    numFailed += safeRoundTest( '-1.2e-3', 5, '-0.0012' );
    numFailed += safeRoundTest( '9876.543e210', 0, '9.876543e+213' );
    numFailed += safeRoundTest( '9876.543e210', -210, '9.877e+213' );
    numFailed += safeRoundTest( '9876.543e210', -209, '9.8765e+213' );
    numFailed += safeRoundTest( '9876.543e+210', 0, '9.876543e+213' );
    numFailed += safeRoundTest( '9876.543e+210', -210, '9.877e+213' );
    numFailed += safeRoundTest( '9876.543e+210', -209, '9.8765e+213' );
    numFailed += safeRoundTest( '9876.543e-210', 213, '9.876543e-207' );
    numFailed += safeRoundTest( '9876.543e-210', 210, '9.877e-207' );
    numFailed += safeRoundTest( '9876.543e-210', 211, '9.8765e-207' );
    console.log( 'numFailed', numFailed );
}

safeRoundTests();
z32a7ul
  • 3,695
  • 3
  • 21
  • 45
  • This appears to be the only 100% correct version. I wrote one similar to yours thinking of posting as an answer but then I saw I this. And it's twice as fast as mine. – BrendanOtherwhyz Jun 27 '22 at 04:29
1

A proper way to avoid the binary issues with rounding to an arbitrary number of places would be:

function roundToDigits(number, digits) {
  return Number(Math.round(Number(number + 'e' + digits)) + 'e-' + digits);
}

A way to fix the toFixed() function would be:

Number.prototype.toFixed = (prototype => {
    const toFixed = prototype.toFixed;

    // noinspection JSVoidFunctionReturnValueUsed
    return function (fractionDigits) {
        if (!fractionDigits) {
            return toFixed.call(this);
        } else {
            // Avoid binary rounding issues
            fractionDigits = Math.floor(fractionDigits);
            const n = Number(Math.round(Number(+this + 'e' + fractionDigits)) + 'e-' + fractionDigits);
            return toFixed.call(n, fractionDigits);
        }
    };
})(Number.prototype);
daniel.gindi
  • 3,457
  • 1
  • 30
  • 36
0

Please use the below function if you don't want to round off.

function ConvertToDecimal(num) {
  num = num.toString(); // If it's not already a String
  num = num.slice(0, (num.indexOf(".")) + 3); // With 3 exposing the hundredths place    
alert('M : ' + Number(num)); // If you need it back as a Number     
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nimesh
  • 3,342
  • 1
  • 28
  • 35
  • You should have the desired decimal places be a second parameter of the function that "defaults" to 2 or 3 decimal places. – Joshua Pinter Jun 22 '16 at 22:36
0

I tried my very own code. Try this:

function AmountDispalyFormat(value) {
    value = value.toFixed(3);
    var amount = value.toString().split('.');
    var result = 0;
    if (amount.length > 1) {
        var secondValue = parseInt(amount[1].toString().slice(0, 2));
        if (amount[1].toString().length > 2) {
            if (parseInt(amount[1].toString().slice(2, 3)) > 4) {
                secondValue++;
                if (secondValue == 100) {
                    amount[0] = parseInt(amount[0]) + 1;
                    secondValue = 0;
                }
            }
        }

        if (secondValue.toString().length == 1) {
            secondValue = "0" + secondValue;
        }
        result = parseFloat(amount[0] + "." + secondValue);
    } else {
        result = parseFloat(amount);
    }
    return result;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Suganth G
  • 5,136
  • 3
  • 25
  • 44
0
number=(parseInt((number +0.005)*100))/100;     

add 0.005 if you want to normal round (2 decimals)

8.123 +0.005=> 8.128*100=>812/100=>8.12   

8.126 +0.005=> 8.131*100=>813/100=>8.13   
Sangram Shivankar
  • 3,535
  • 3
  • 26
  • 38
Elid Garazlic
  • 121
  • 1
  • 5
0

I created this function, for rounding a number. The value can be a string (ex. '1.005') or a number 1.005 that will be 1 by default and if you specify the decimal to be 2, the result will be 1.01

round(value: string | number, decimals: number | string = "0"): number | null {
    return +( Math.round(Number(value + "e+"+decimals)) + "e-" + decimals);
}

Usage: round(1.005, 2) // 1.01 or Usage: round('1.005', 2) //1.01

chriscrossweb
  • 338
  • 1
  • 5
  • 23
0

Use something like this to round up:

num = 519.805;
dp = Math.pow(10, 2);
num = parseFloat(num.toString().concat("1"));
rounded = Math.round((num + Number.EPSILON)* dp)/dp;

As it would deal with numbers falling short where there is only one decimal place to round at the end.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
shahroz butt
  • 168
  • 1
  • 7
0

Here I used a ternary operator to check if the number has fractional values. If it doesn't then I simply return the number.

Otherwise, I use the Intl.NumberFormat constructor to get the desired value.

Intl.NumberFormat is part of the ECMAScript Internationalization API Specification (ECMA402). It has pretty good browser support, including even IE11, and it is fully supported in Node.js.

const numberFormatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

function getRoundedNumber(number) {
  return number.toString().indexOf(".") == -1 ? number : numberFormatter.format(number);
}


console.log(getRoundedNumber(10));
console.log(getRoundedNumber(1.7777777));
console.log(getRoundedNumber(9.1));
console.log(getRoundedNumber(2.345));
console.log(getRoundedNumber(2.2095));
console.log(getRoundedNumber(2.995));
samnoon
  • 1,340
  • 2
  • 13
  • 23
0

Only if necessary you said?

If you care about negatives numbers too, I suggest you this...

Some answers don't work well with negative numbers...

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>roundPrecision</title>
    <script>
        class MyMath{
            static roundPrecision(number, precision, fillZeros) {
                // Number you want to round
                // precision nb of decimals
                // fillZeros the number of 0 You want to add IF necessary!
                // 0 = no fill with zeros.
                let num = number;
                let prec = precision;
                let exp = Math.pow(10, prec);
                let round = Math.round(number * exp)/exp
                if (fillZeros>0) {
                    return round.toFixed(fillZeros)
                }
                return round;
            }
        }
    </script>
</head>

<body>
    <p class="myMath" id="field1"></p>
    <p class="myMath" id="field2"></p>
    <p class="myMath" id="field3"></p>
    <p class="myMath" id="field4"></p>
    <p class="myMath" id="field5"></p>
    <p class="myMath" id="field6"></p>
    <p class="myMath" id="field7"></p>
    <script>
        document.getElementById("field1").innerHTML = MyMath.roundPrecision(5, 0, 3); // 5.000
        document.getElementById("field2").innerHTML = MyMath.roundPrecision(Math.PI, 2, 4); // 3.1400
        document.getElementById("field3").innerHTML = MyMath.roundPrecision(2.4, 1, 2); // 2.40
        document.getElementById("field4").innerHTML = MyMath.roundPrecision(2.9, 0, 2);   // 3.00
        document.getElementById("field5").innerHTML = MyMath.roundPrecision(10, 0, 2); // 10.00
        document.getElementById("field6").innerHTML = MyMath.roundPrecision(-10.5, 1, 2); // 10.00
        document.getElementById("field7").innerHTML = MyMath.roundPrecision(-1.006, 2, 0); // 10.00
    </script>
</body>
</html>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tatactic
  • 1,379
  • 1
  • 9
  • 18
0

const formattedNumber = Math.round(number * 100) / 100;

Hamza Dahmoun
  • 1,204
  • 10
  • 16
  • An explanation would be in order, especially of such a short answer on page 3 of answers (that is, after more than 60 answers). Isn't it covered by a previous answer? What is the idea/gist? From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/73964191/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Oct 19 '22 at 01:56
  • Does it work for 1.015? – Peter Mortensen Oct 19 '22 at 03:01
-9

I still don't think anyone gave him the answer to how to only do the rounding if needed. The easiest way I see to do it is to check if there is even a decimal in the number, like so:

var num = 3.21;
if ( (num+"").indexOf('.') >= 0 ) { //at least assert to string first...
    // whatever code you decide to use to round
}
coreyavis
  • 87
  • 1
  • 7