94

I'm using javascript to bind to some checkboxes, and the toFixed(2) is not rounding up. Any ideas why it's not rounding? For instance, if the number is 859.385 it's only displaying 859.38 instead of 859.39.

I've also read that the toFixed can round differently depending on which browser you are using, anyone know of a way around this so that my javascript calculations match my php calculations?

var standardprice = parseFloat($('#hsprice_'+this.id.split('_')[1]).val());
var price =  parseFloat($('#hprice_'+this.id.split('_')[1]).val());
var discount =  parseFloat($('#hdiscount_'+this.id.split('_')[1]).val());
var deposit =  parseFloat($('#hdeposit_'+this.id.split('_')[1]).val());

var currSprice = parseFloat($('#hTotalSprice').val());
var currPrice = parseFloat($('#hTotalPrice').val());
var currDiscount = parseFloat($('#hTotalDiscount').val());
var currDeposit = parseFloat($('#hTotalDeposit').val());

currSprice += standardprice;
currPrice += price;
currDiscount += discount;
currDeposit += deposit;

$('#lblTotalSprice').text('$'+addCommas(currSprice.toFixed(2)));
$('#lblTotalPrice').text('$'+addCommas(currPrice.toFixed(2)));
$('#lblTotalDiscount').text('$'+addCommas(currDiscount.toFixed(2)));
$('#lblTotalDeposit').text('$'+addCommas(currDeposit.toFixed(2)));

$('#hTotalSprice').val(currSprice.toFixed(2));
$('#hTotalPrice').val(currPrice.toFixed(2));
$('#hTotalDiscount').val(currDiscount.toFixed(2));
$('#hTotalDeposit').val(currDeposit.toFixed(2));
6
  • 1
    Since 0.5 is exactly halfway between 0 and 1 and rounding up is only a convention, I wonder how important it really is to guarantee a specific result. On the other hand, in order to test your code, you need predictable results and testing is important, so that's a good reason. Commented May 9, 2014 at 17:44
  • 2
    Here's a hint as to why the rounding of .toFixed can seem unintuitive: (0.1).toFixed(20) . (Note that IE's implementation gives the "intuitive" result, while other browsers give the standards-compliant value.) Commented May 17, 2015 at 20:30
  • 1
    My answer for similar question here: stackoverflow.com/a/37751946/2261514 Commented Jun 10, 2016 at 15:45
  • Intl.NumberFormat rounding works. It could be used wherever num.isFixed() is used. see Formatting a number with exactly two decimals in js Commented Jan 20, 2024 at 4:37
  • @Noyo sorry for 9 year necro but I don't know that .toFixed(20) is a sensible thing to ever do with IEEE754 doubles. Because, 53 bits of mantissa can only give you log_10(2^53) = 15.95 base-10 digits worth of precision to work with... Going beyond that is going to have the number renderer (in this case .toFixed) to somewhat hallucinate digits. 0.1.toFixed(20) = '0.10000000000000000555' may be a "correct" decimal representation of whatever the closest double value to 0.1 is, and the same might be said for 0.1.toFixed(30): 0.100000000000000005551115123126 but this is not useful. Commented Feb 6, 2024 at 5:55

25 Answers 25

47

I have yet to find a number that toFixed10 does wrong. Can anybody else?

Thanks to blg and his answer which pointed me to Mozilla's toFixed10() method.

Using that I came up with this short one liner, which indeed covers all cases mentioned here...

function toFixed( num, precision ) {
    return (+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision);
}
Sign up to request clarification or add additional context in comments.

9 Comments

What/where is toFixed10? Please add a link to it?
Number 0.0000001 or less results in NaN
Nope, this is still imprecise, toFixed(89.684449, 2) should be 89.69, but is 89.68.
@mrts toFixed(89.684449, 2) is correctly 89.68, it would be only 69 if it was 89.685<any other irrelevant digits>
On toFixed(0.000000015, 8) get NaN. For correct answer 0.00000002 I use this function function round(v,d){return parseFloat(Math.round(v.toFixed(d+1)+'e'+d)+'e-'+d)};
|
30

I made this to use in all financial data as a best rounding function. You can test it on all problematic numbers. Javascript allows some sort of precision, so I used it to make almost every number be rounded as expected.

function roundTo(n, digits) {
        if (digits === undefined) {
            digits = 0;
        }

        var multiplicator = Math.pow(10, digits);
        n = parseFloat((n * multiplicator).toFixed(11));
        return Math.round(n) / multiplicator;
    }

1 Comment

Umm the comment is misleading, roundTo(89.684449, 2) shouldn't be 89.69, and is legitimately 89.68.
27

In Chrome, toFixed() rounds:

859.385 ==> 859.38
859.386 ==> 859.39

When I look at the ECMAScript 5th edition specification for .toFixed() (section 15.7.4.5), I do not see it explicitly describe rounding though it does describe something fairly obtusely that may be what Chrome has implemented.

It appears to me that if you want to control it with explicit rounding, then you should probably use the oft-suggested workaround of:

var roundedNum = (Math.round( num * 100 ) / 100).toFixed(2);

This will guarantee that you get predictable rounding like you are used to.

Working demo here: http://jsfiddle.net/jfriend00/kvpgE/

5 Comments

It'll normally guarantee that you get predictable rounding. However, as mentioned in this answer the way javascript handles decimals can't really be trusted. Just try 35.855*100 in the Chrome console... I know, I was shocked too! This will mean that (Math.round( 35.855 * 100 ) / 100).toFixed(2) == 35.85 rather than 35.86. See that other answer for tips...
@mattbilson This is standard IEEE floating point and doesn't mean JavaScript is untrustworthy in this regard. Floating point numbers just aren't the real numbers (mathematical sense) but are entirely predictable.
This is not a perfect answer. If you input 1.005, you'll get 1.00 but it should be 1.01.
@foxiris - Yep, due to the wonders of floating point, 1.005 * 100 comes out to be 100.49999999999999 which derails the rest of the algorithm.
A decimal float will always be <= the actual number. It is best to always add something smaller than the smallest precision needed. In this case we need 859.385 so you would add something like 0.000001 to the float: (859.385 + 0.000001).toFixed(2)
24

Since in javascripts' toFixed-function, the floating point number5 does not belong to the upper half of an integer, the given number is rounded down if you have numbers like these:

859.385.toFixed(2) // results in 859.38

for a matter of fact you might append trailing floating point numbers (except zero) like here:

859.3851.toFixed(2) // results in 859.39

Therefore developers tend to add numbers like 0.00000000001 in order for it to be rounded appropriate and to not accidentally change the value of the number.

So I came up with a function which adds such a number depending on how many digits you want your floating point number to be fixed:

    // both parameters can be string or number
    function toFixed(number, decimals) {
        const x = Math.pow(10, Number(decimals) + 2);
        return (Number(number) + (1 / x)).toFixed(decimals)
    }
    toFixed(859.385, 2) //results in 859.39
    toFixed(859.3844, 2) //results in 859.38

5 Comments

Best answer I could find! Easy code to read and it even handle double rounding!
toFixed(859.3844, 2) gives 859.39, but should be 859.38
toFixed(.004, 2); == .00 ... not .01
Usually we know what precision we want to round to. Let's say we want num.toFixed(2). Then adding .0001 to num gets the right answer from toFixed. While a general function is fine, it seems to me to be a bit of overkill, unless the number of desired digits is a variable -- an unlikely circumstance.
The idea is good but there is a mistake. The added fraction is too big and because of it even 0.0046 is rounded to 0.01. Changing the intermediate line to var x = Math.pow(10, Number(decimals) + 2); helps resolving the issue
17

toFixed was never rounding nor meant for it.

It barely converts a floating point (exponent, mantissa) into a fixed-point (integer, decimals) (see definition).

While doing this, it might look as if it's rounding, because the rounded number is the closest approximation to the original floating-point representation.

Sadly, it is widely spread that toFixed can be used for rounding... well no it cannot, even the name suggests has nothing to do with it.

If you need rounding plus the toFixed padding:

function round(num, precision) {
  var base = 10 ** precision;
  return (Math.round(num * base) / base).toFixed(precision);
}

2 Comments

Someone get this guy a cookie. This is the proper way to round currency.
round(148.325, 2) gives '148.32' because 148.325 * 100 is 14832.499999999998
10

Another good number to try along with 35.855 is 1.005

I don't think Robert Messerle's solution handles 1.005

The rounding decimal example here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round$revision/1383484#Decimal_rounding converts numbers to exponential notation and seems to get better results.

I created a fiddle here http://jsfiddle.net/cCX5y/2/ that demos the native, Robert Messerle example above (called toFixedB) and the one from Mozilla docs (called toFixed10).

I have yet to find a number that toFixed10 does wrong. Can anybody else?

4 Comments

Good fiddle and link. I added user2823670's one-liner: jsfiddle.net/cCX5y/3
Looks like this answer also works and doesn't involve all the extra code.
All of them are imprecise, toFixed10(89.684449, 2) and others should be 89.69, but is 89.68.
I think you are double rounding. (en.wikipedia.org/wiki/Rounding#Double_rounding). Any number such as 1.2345 will round to 1.23 not 1.24
9
function roundup(num,dec){
    dec= dec || 0;
    var  s=String(num);
    if(num%1)s= s.replace(/5$/, '6');
    return Number((+s).toFixed(dec));
 }

 var n= 35.855
 roundup(n,2)

/* returned value: (Number) 35.86 */

3 Comments

Try it with 35.855.
This is the only solution I've seen that will round predictably.
The only problem is, it doesn't work if one of the decimals is 5 and the number of decimals to round to is higher than the position of that 5, e.g. roundup(70.5, 2). This could easily be improved by changing the regular expression in the replace statement, to something like: s.replace(/(\d{n})5/, '$16'), with n being equal to the dec paraemter. We only need to change the 5 in the n+1th position behind the decimal point, when rounding to n decimals, am I right?
9

toFixed() works correctly! The problem is, that 859.385 has no representation as float at a computer. It is scanned as nearest possible value = 859.384999999999991. And rounding this value to 2 digits is 859.38 and not 859.39.

This is the reasons, that many programming languages (especially old for commerce, e.g. COBOL) support BCD numbers (binary coded decimals), where each digit is coded by itself into 4 bits (like hex without using A-F).

A general solution for prices: Calculate in cent/penny and print NUMBER/100.

A note to other solutions (functions provided here): They may help for some numbers, but mostly fail for e.g. 859.38499999.

1 Comment

I put link here for more information see
7

You can use the Math.round() to round the number. If you want to round to a specific decimal point you can employ a little math:

var result=Math.round(original*100)/100

2 Comments

Note that this is unreliable. Try it with 35.855, for instance, and you'll end up with 35.85 (not 35.86).
noe that this was just an example. insert for the 100 Math.pow(10, decimalpoint) where decimalpoint is the numbers of floating point digits you want to round your number
7

If you are looking to get a number as output, then consider the Math.round() technique in other answers.

But if you want to get a string as output, for presentation to a human, then often n.toLocaleString() is more helpful than n.toFixed().

Why? Because it will also add commas or periods to the head of large numbers, which humans use to read. For example:

var n = 1859.385

n.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})

// Produces  1,859.39  in USA (locale en-US)
// Produces  1 859,39  in France (locale fr-FR)
// Produces  1.859,39  in Germany (locale de-DE)

The spec says that when passing undefined as the first argument, the user's own locale will be used (as specified by the OS). Unfortunately, as the linked documentation shows, Mozilla uses the en-US locale in this situation, but it may comply with the spec in future.

1 Comment

It works also for negative numbers that is good
6

I ran into this same problem today, and even trying suggestions in other answers I found that I still did not receive the result I expected. Finally, as I'm using AngularJS for my current project when running into this, I figured I check if AngularJS has already solved the same kind of problem before and indeed they had. Here's the solution they use and it works perfectly for me:

function toFixed(number, fractionSize) {
    return +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
}

Found here: AngularJS filters.js source

1 Comment

This is identical to this answer except AngularJS doesn't have the trailing .toFixed()
4

I stumbled upon this wondering why Number.toFixed was behaving strangely. I see that the native function is unreliable, which is unfortunate. Looking over the answers out of curiosity, I see most* of them don't behave properly with the number 35.855 as T.J. Crowder graciously commented on every one.

Maybe this will answer your question.

function toFixed(n,precision) {
    var match=RegExp("(\\d+\\.\\d{1,"+precision+"})(\\d)?").exec(n);
    if(match===null||match[2]===undefined) {
        return n.toFixed(precision);
        }
    if(match[2]>=5) {
        return (Number(match[1])+Math.pow(10,-precision)).toFixed(precision);
        }
    return match[1];
    }

The regex splits your number into an array of strings such as in toFixed(35.855,2): ["35.855", "35.85", "5"]. If the last number (after the precision cutoff) is >=5, add Math.pow(10, -precision) to the trimmed number. This will add .01 if you're cutting off at 2 decimals, .002 at 3, so on and so forth.

I don't know if this is foolproof, since it still performs decimal math on floats which can be unpredictable. I can say it rounds 35.855 up to 35.86.

Comments

4

Joy twindle answer here should be the best answer instead of the so many hack arounds.

   x = 1859.385;
   x = x.toFixed(2);
   alert(x);

gives a wrong rounding off ie 1859.38 instead of 1859.39

 x = 1859.385;
 x = x.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
 alert(x);

gives a correct rounding off 1,859.39

The only problem is the result returned is a string with thousand comma separator and so cannot be used for calculations. Using a regex i got from stack overflow to remove the comma the final result is

 x = 1859.385;
 x = x.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
 x=x.replace(/\,/g,'');
 alert(x);

that now returns 1859.39 and can be used for calculations. The value before regex ie 1,859.39 can be used for html display while the un-formatted value 1859.39 can be used for calculations.

2 Comments

The first argument is for the locale string. Ex. 'en-US'
@webs The "comma" is a "thousands separator" and differs if you do not specify a locale as first argument like "en-US". So this solution will only work in countries where they use a comma as thousands separator. Instead of using your regex you can just turn this off with the option useGrouping: false. E.g.: .toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: false})
4

Note: You may only need this .toFixed() solution for handling very large numbers

Other posted solutions work well for standard numbers (below 1e21)

If you want to handle very large numbers and very small fractions with toFixed with correct rounding and without loss of accuracy and without having to use libraries and with a self-contained function, one possible effective solution is to make use of the BigInt to avoid loss of accuracy due to internal javascript rounding.

The function below toFixed(number, [digits]) does this.

Just pass the large number as a string with the required number of rounding digits. Numbers will be rounded properly.

The concept is the common way to split the number into the whole part and the decimal part but use BigInt() to hold these 2 parts. Thereafter, round and process as necessary for both positive and negative numbers without using any of the javascript Math or number functions to avoid loss of accuracy.

As a bonus for very special cases where the number is in a very large 'e' notation, I have added the eToNumber() function I have posted here: https://stackoverflow.com/a/66072001/11606728 to do that as an inline process. This may be removed if you want to maintain shortcode and do not bother about such numbers. [comments are provided in the code to remove it].

I have included various test cases to test the different possibilities (some of which are a bit odd but nevertheless do occur).

This idea can be taken further for improvement.

Hope it is useful.

/****************************************************************************
* @function    : toFixed(number, [digits])
* @purpose     : Emulate toFixed() for large numbers and
                 large fractional numbers with correct rounding
                 by using the BigInt() built-in Object.
* @version     : 1.01
* @author      : Mohsen Alyafei
* @date        : 03 February 2021
* @param       : {number} [numeric or string] pass large numbers as a string
* @param       : {number} [optional digits]
*              : The number of digits to appear after the decimal point;
*              : this may be a value from 0 and above unlimited.
*              : If this argument is omitted or is negative, it is treated as 0.
*              : Handles large 'e' notation number using the eToNumber() function.digits
*              : See https://stackoverflow.com/a/66072001/11606728
* @returns     : A string representing the given number using fixed-point notation.
****************************************************************************/

function toFixed(num,digits) {
    if (!num && num!==0) return "Cannot read property of null or undefined"; // Can change it to throw Error
    digits<0 && (digits=0);
    digits=digits||0;
    num=eToNumber(num); // Delete this line and function below if larage 'e' notation number are not required
    let wh = (num+="").split((.1).toLocaleString().substr(1,1)), f = wh[1];
    wh = wh[0];
    let fr = (f=f||"0").substr(0,digits), fn = BigInt(fr), w = BigInt(wh), fl = (""+fn).length,
        lZeros = fr.length-fl, minus = wh[0]=="-", inc = (wh<0 || minus) ? BigInt(-1):BigInt(1);
    f[digits]>=5 && (fn+=BigInt(1));
    (fn+="").length > fl && (lZeros-=1);
    lZeros >=0 ? lZeros="0".repeat(lZeros):(fn=fn.substring(1), lZeros="",
               (fn ? w +=inc : ((f[digits]>=5) && (w+=inc))));
    fn = lZeros + fn; L = digits-fn.length;
    L && (fn+="0".repeat(L)); w==0 && minus && (w="-"+w);
    return w+(fn?".":"")+fn;
    }

    //---------------------- Extra Function if needed --------------------------------
    // Delete this function if large 'e' notation number are not required
    // Convert very large 'e' numbers to plain string numbers.
    //--------------------------------------------------------------------------------
    function eToNumber(num) {
        let sign="";
        (num+="").charAt(0)=="-" && (num=num.substring(1),sign ="-");
        let arr = num.split(/[e]/ig); if (arr.length<2) return sign+num;
        let dot=(.1).toLocaleString().substr(1,1), n = arr[0], exp = +arr[1];
        let w = (n=n.replace(/^0+/,'')).replace(dot,''),
          pos = n.split(dot)[1]? n.indexOf(dot)+exp : w.length+exp,
            L = pos-w.length,s=""+BigInt(w);
         w = exp>=0 ? (L>=0 ? s+"0".repeat(L):r()): (pos<=0 ? "0"+dot+"0".repeat(Math.abs(pos))+s:r());
        if (!+w) w=0; return sign+w;
        function r(){return w.replace(new RegExp(`^(.{${pos}})(.)`),`$1${dot}$2`)}}

//================================================
//             Test Cases
//================================================
let r=0; // test tracker
r |= test(35.855,2,"35.86");
r |= test(12.00000015,2,"12.00");
r |= test(35.855,5,"35.85500");
r |= test(35.855,4,"35.8550");
r |= test(1.135,2,"1.14");
r |= test(1.135,3,"1.135");
r |= test(1.135,4,"1.1350");
r |= test(1.135,8,"1.13500000");
r |= test(0.1545,3,"0.155");
r |= test(89.684449,2,"89.68");
r |= test("0.0000001",2,"0.00");
r |= test("0.9993360575508052",3,"0.999");
r |= test("0.999336057550805244545454545",29,"0.99933605755080524454545454500");
r |= test("1.0020739645577939",3,"1.002");
r |= test(0.999,0,"1");
r |= test(0.999,1,"1.0");
r |= test(0.999,2,"1.00");
r |= test(0.975,0,"1");
r |= test(0.975,1,"1.0");
r |= test(0.975,2,"0.98");
r |= test(2.145,2,"2.15");
r |= test(2.135,2,"2.14");
r |= test(2.34,1,"2.3");
r |= test(2.35,1,"2.4");
r |= test("0.0000001",2,"0.00");
r |= test("0.0000001",7,"0.0000001");
r |= test("0.0000001",8,"0.00000010");
r |= test("0.00000015",2,"0.00");
if (r==0) console.log("Tests 01. Standard fractional numbers passed");
//================================================
r=0; // test tracker
r |= test("1234567890123456789444.99",5,"1234567890123456789444.99000");
r |= test("1234567890123456789444.1445",3,"1234567890123456789444.145");
r |= test("1234567890123456789444.14451445144514451745",19,"1234567890123456789444.1445144514451445175");
if (r==0) console.log("Tests 02. Large fractional numbers passed");
//================================================
r=0; // test tracker
r |= test(100,2,"100.00");
r |= test(0,5,"0.00000");
if (r==0) console.log("Tests 03. Non-fractional numbers passed");
//================================================
r=0; // test tracker
r |= test(12345.6789,null,"12346");
r |= test(2.1234,null,"2");
r |= test(12345.6789,undefined,"12346");
r |= test(2.1234,undefined,"2");
r |= test(12345.6789,"","12346");
r |= test(0.1234,"","0");
r |= test(2.1234,"","2");
if (r==0) console.log("Tests 04. Undefined, Null, and Empty Digits passed");
//================================================
r=0; // test tracker
r |= test(1.1155,2,"1.12");
r |= test(1.255,2,"1.26");
r |= test(1.265,2,"1.27");
r |= test(1.275,2,"1.28");
r |= test(1.285,2,"1.29");
r |= test(1.295,2,"1.30");
r |= test(2.05,1,"2.1");
r |= test(2.15,1,"2.2");
r |= test(2.55,1,"2.6");
r |= test(2.65,1,"2.7");
r |= test(2.215,2,"2.22");
r |= test(2.315,2,"2.32");
r |= test(2.715,2,"2.72");
r |= test(2.815,2,"2.82");
r |= test(2.005,2,"2.01");
r |= test(2.105,2,"2.11");
r |= test(2.405,2,"2.41");
r |= test(2.505,2,"2.51");
r |= test(2.605,2,"2.61");
r |= test(2.905,2,"2.91");
r |= test(0.00155,4,"0.0016");
r |= test(2.55,1,"2.6");
r |= test(-2.35,1,"-2.4");
if (r==0) console.log("Tests 05. Correct rounding passed");
//================================================
r=0; // test tracker
r |= test(-1.125,2,"-1.13");
r |= test(-1.15,1,"-1.2");
r |= test(-1.15,1,"-1.2");
r |= test(-1.45,1,"-1.5");
r |= test(-1.65,1,"-1.7");
r |= test(-1.95,1,"-2.0");
r |= test(-2.34,1,"-2.3");
r |= test("-0.024641163062896567",3,"-0.025");
r |= test("-0.024641163062896567",16,"-0.0246411630628966");
r |= test("0.024641163062896567",16, "0.0246411630628966");
r |= test("-0.0246411630628965",16,"-0.0246411630628965");
if (r==0) console.log("Tests 06. Negative numbers rounding passed");
//================================================
r=0; // test tracker
r |= test(.135,2,"0.14");    // without whole part
r |= test(-.135,2,"-0.14");
r |= test("+35.855",2,"35.86");
r |= test("0.0",2,"0.00");
r |= test("-0",2,"-0.00");       //minus 0
r |= test("-0.0",5,"-0.00000");  // minus 0
r |= test("",5,"Cannot read property of null or undefined");        // empty string
r |= test(null,5,"Cannot read property of null or undefined");      //null
r |= test(undefined,5,"Cannot read property of null or undefined"); // undefined
if (r==0) console.log("Tests 07. Special test cases passed");
//================================================
r=0; // test tracker
r |= test(1.1234e1,2,"11.23");      //11.234
r |= test(1.12e2,2,"112.00");       //112
r |= test(-1.1234e2,2,"-112.34");   // -112.34
r |= test(-1.1234e2,4,"-112.3400"); // -112.34
r |= test(-1.1235e2,2,"-112.35");   // -112.35
r |= test(-1.1235e2,1,"-112.4");    // -112.4
if (r==0) console.log("Tests 08. Standard e notation numbers passed");
//================================================

r=0; // test tracker
r |= test("123456789123456789.111122223333444455556666777788889999e+10",16,"1234567891234567891111222233.3344445555666678");
r |= test("1.1235678944556677e2",20,"112.35678944556677000000");
r |= test("99.1235678944556677e2",20,"9912.35678944556677000000");
if (r==0) console.log("Tests 09. Large e notation numbers passed");
//================================================

if (r==0) console.log(`${"-".repeat(22)}\nAll Test Cases Passed.\n${"-".repeat(22)}`);

//================================================
//             Test function
//================================================
function test(n1,n2,should) {
let result = toFixed(n1,n2);
if (result !== should) {console.log(`Output   : ${result}\nShould be: ${should}`);return 1;}
}

Comments

3

this might help

    tofix2Decimals=function(float){
        if(parseInt(float)==float)return float.toFixed(2);
        $decimals=/\.(\d+)/.exec(float)[1].length;
        $decimals=$decimals>=2?$decimals+1:3;
        float+=Math.pow(10,-$decimals);
        return float.toFixed(2);
    }

2 Comments

An explanation might help.
A fine and fancy trip to the obfuscating rink. Ugh.
3

I know this is an old question, but why not doing something like this:

let TruncatedValueInString = ThenNumberYouWantToTruncate.toFixed(decPlaces + 1).slice(0, -1);

Comments

3

I wanted something concise and accurate with a good combination of readable and speed. After reading all the answers to this question, and this particularly helpful answer in a similar question, this is my solution.

const round = (numberToRound, digits = 0, toFixed = false) => {
  const precision = 10 ** digits;
  const n = numberToRound * precision * (1 + Number.EPSILON);
  const roundedNumber = Math.round(n) / precision;
  return toFixed ? roundedNumber.toFixed(digits) : roundedNumber;
};

// rounding by half
console.log(round(0.5));
console.log(round(-0.5));

// fixed decimals
console.log(round(0.5, 2), '( Type:',typeof round(0.5, 2), ')');
console.log(round(0.5, 2, true), '( Type:',typeof round(0.5, 2, true), ')');
console.log(round(-0.5, 2), '( Type:',typeof round(-0.5, 2), ')');
console.log(round(-0.5, 2, true), '( Type:',typeof round(-0.5, 2, true), ')');

// edge cases
console.log(round(1.005, 2) === 1.01);
console.log(round(-1.005, 2) === -1.01);
console.log(round(39.425, 2) === 39.43);
console.log(round(-39.425, 2) === -39.43);
console.log(round(1234.00000254495, 10) === 1234.000002545);
console.log(round(-1234.00000254495, 10) === -1234.0000025449);

// edge cases from other answer's comments.
console.log(round(859.385, 2));
console.log(round(859.3844, 2));
console.log(round(0.000000015, 8))
console.log(round(35.855, 2));

I don't love toFixed as a boolean parameter but it works for now.

Comments

3

The toFixed() method in JavaScript does not follow the standard rounding rules for numbers ending in .5. It uses the "round half up" strategy, which means it rounds up when the next digit is 5 or greater.

If you need to round numbers ending in .5 in a more consistent way, you can create a custom rounding function. Here's an example:

function roundNumber(number, decimalPlaces) {
  const factor = 10 ** decimalPlaces;
  return Math.round(number * factor) / factor;
}

const number = 3.145;
const roundedNumber = roundNumber(number, 2);

console.log(roundedNumber); // Output: 3.15

Alternatively, you can use toLocaleString() method to achieve this. Here's another example:

const number = 3.145;
const locales = window.navigator.language;
const options = { maximumFractionDigits: 2 };
const roundedNumber = Number(number.toLocaleString(locales, options));

console.log(roundedNumber); // Output: 3.15

1 Comment

This does not work if e.g. window.navigator.language is de-DE, because this locale uses a , instead of . as decimal separator, but the Number() constructor requires the decimal separator to be a .. You should make the locale constant, e.g. "en-US".
2

I have got proper solution from Lam Wei Li

function round(number, precision) {
  var shift = function (number, exponent) {
    var numArray = ("" + number).split("e");
    return +(numArray[0] + "e" + (numArray[1] ? (+numArray[1] + exponent) : exponent));
  };
  return shift(Math.round(shift(number, +precision)), -precision);
}

Test Result

round(1.050, 1); // expected 1.1 , result 1.1  (correct)
round(1.005, 2); // expected 1.01, result 1.01 (correct)

round(3456.3456,  3); // 3456.346
round(3456.3456,  2); // 3456.35
round(3456.3456,  1); // 3456.3
round(3456.3456,  0); // 3456
round(3456.3456, -1); // 3460
round(3456.3456, -2); // 3500
round(3456.3456, -3); // 3000

round(undefined, 1        ); // NaN
round(null     , 1        ); // NaN
round("a"      , 1        ); // NaN
round(1        , null     ); // NaN
round(1        , undefined); // NaN
round(1        , "a"      ); // NaN

Comments

2
function toFixed(num, decimals) {
    return (Math.round((num + Number.EPSILON) * Math.pow(10, decimals)) / Math.pow(10, decimals)).toFixed(decimals)
}

I've found a valid answer here -> Round to at most 2 decimal places (only if necessary)

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

You have to add Number.EPSILON to the rounded number.

Comments

0

Here is my solution, including 3 features.

  1. Correct round up.
  2. Display only in number. (Preventing Scientific Notation)
  3. Removes trailing zeros.

I combined @user2823670's answer and this together.

var roundUp = function(num, precision) {
    // Return '' if num is empty string
    if (typeof num === 'string' && !num) return '';

    // Remove exponential notation
    num = toPlainString(num);

    // Fixed round up
    var result = +((+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision));

    // Remove exponential notation (once again)
    result = toPlainString(result);

    return result;
};

var toPlainString = function(num) {
    return ('' + num).replace(/(-?)(\d*)\.?(\d+)e([+-]\d+)/,
        function(a, b, c, d, e) {
            return e < 0
                ? b + '0.' + Array(1 - e - c.length).join(0) + c + d
                : b + c + d + Array(e - d.length + 1).join(0);
        }
    );
}

3 Comments

The second function toPlainString does not work properly. Still, the following numbers do not convert 1e-7 it should be 0.0000001 but gives 0.00000001 i.e. with one more zero inserted. Similarly for 1e-8, 1e-9 and higher.
change the regex to the following replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/ to get it working.
An alternative solution for converting 'e' notation numbers (keeping accuracy) for large numbers and large fractions above 1^21 can be found here stackoverflow.com/a/66072001/11606728
0

This worked for me - "hackish"

function customTofix(value, precision) {
    let decimalVal = 0;
    
    if (value !== null) {
        let appendValue = (((value - Math.floor(value)) !== 0) ? (precision <= (value.toString().split(".")[1].length || 0)) ? '1' : '' : '');
        decimalVal = parseFloat(value.toString() + appendValue).toFixed(precision)
    }

    return decimalVal
}

Comments

0

My workout:

For me, This was enough to include in common javascript like helpers.js

// eslint-disable-next-line no-extend-native
Number.prototype.toFixed = function (fractionDigits) {
    var precisionTens = Math.pow(10, fractionDigits || 0);
    return (Math.round(Number(this) * precisionTens) / precisionTens);
}

This will overwrite the native javascript toFixed() prototype function.

Comments

0

I spent way too long on this.

export function parseFloat<TDefault>(nbr: string | number, defaultValue: TDefault): number | TDefault
export function parseFloat(nbr: string | number): number | null
export function parseFloat(nbr: string | number, defaultValue: any = null): number | null {
    if(nbr == null || nbr === '') return defaultValue
    const val = globalThis.parseFloat(nbr as string)
    return Number.isFinite(val) ? val : defaultValue
}

export function fullWide(n: number|string):string {
    return parseFloat(n,0).toLocaleString('en-US', {useGrouping: false, maximumFractionDigits: 20})
}

export function round(value: string | number, decimalDigits = 0): number {
    return +(`${Math.round(+(`${fullWide(value)}e${decimalDigits}`))}e${-decimalDigits}`)
}

Passes all these tests:

test('round', () => {
    expect(round(1.2345, 0)).toBe(1)
    expect(round(1.2345, 1)).toBe(1.2)
    expect(round(1.2345, 2)).toBe(1.23)
    expect(round(1.2345, 3)).toBe(1.235)
    expect(round(1.2345, 4)).toBe(1.2345)
    expect(round(3.141592653589793, 15)).toBe(3.141592653589793)
    expect(round(3.141592653589793, 13)).toBe(3.1415926535898)
    expect(round(12.345, -1)).toBe(10)
    expect(round(4_500, -3)).toBe(5000)
    expect(round(89.684449, 2)).toBe(89.68)
    expect(round(89.685, 2)).toBe(89.69)
    expect(round(0.000000015, 8)).toBe(0.00000002)
    expect(round(1e-20, 20)).toBe(1e-20)
    expect(round(1.5e-19, 20)).toBe(0.00000000000000000015)
    expect(round(1.5e-19, 19)).toBe(0.0000000000000000002)
    expect(round('1.5e-19', 19)).toBe(0.0000000000000000002)
    expect(round('bacon', 19)).toBe(0)
    expect(round(100.695, 2)).toBe(100.70)
})

1 Comment

@mpen. I agree with you. I wasn't trying to say the other answers are wrong. I was trying to answer the exact use case for the question and felt it was the right decision to make based on the question and the problem they wanted to solve. Looking back in retrospect well...let's say I wouldn't want to take on the wrath of the community again...LOL.
0

Round using Intl.NumberFormat

function roundTo(number, maximumDecimalDigits = 2) {
  const options = {
    minimumFractionDigits: 2,
    maximumFractionDigits: maximumDecimalDigits
  };
  const result = new Intl.NumberFormat('en-US', options).format(number);
  return parseFloat(result.replace(/,/g, ''));
};

How to use

roundTo(122.445, 2) // 122.45

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.