最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript toFixed Not Rounding - Stack Overflow

programmeradmin6浏览0评论

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

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));
Share Improve this question edited Apr 4, 2012 at 16:11 Shawn Chin 86.7k20 gold badges166 silver badges193 bronze badges asked Apr 4, 2012 at 16:06 gandjyargandjyar 1,1932 gold badges10 silver badges15 bronze badges 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. – David Winiecki 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.) – Noyo Commented May 17, 2015 at 20:30
  • 1 My answer for similar question here: stackoverflow.com/a/37751946/2261514 – Pawel 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 – tinystone 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. – Steven Lu Commented Feb 6, 2024 at 5:55
 |  Show 1 more comment

24 Answers 24

Reset to default 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);
}

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

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/

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

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

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?

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 */

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.

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

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.

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

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.

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.

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

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

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

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

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

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

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.

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.

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

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
}

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.

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)
})
发布评论

评论列表(0)

  1. 暂无评论