Formatting a number with exactly two decimals in JavaScript

后端 未结 30 2098
广开言路
广开言路 2020-11-21 06:29

I have this line of code which rounds my numbers to two decimal places. But I get numbers like this: 10.8, 2.4, etc. These are not my idea of two decimal places so how I can

相关标签:
30条回答
  • 2020-11-21 07:01

    To format a number using fixed-point notation, you can simply use the toFixed method:

    (10.8).toFixed(2); // "10.80"
    
    var num = 2.4;
    alert(num.toFixed(2)); // "2.40"
    

    Note that toFixed() returns a string.

    IMPORTANT: Note that toFixed does not round 90% of the time, it will return the rounded value, but for many cases, it doesn't work.

    For instance:

    2.005.toFixed(2) === "2.00"

    UPDATE:

    Nowadays, you can use the Intl.NumberFormat constructor. It's 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 formatter = new Intl.NumberFormat('en-US', {
       minimumFractionDigits: 2,      
       maximumFractionDigits: 2,
    });
    
    console.log(formatter.format(2.005)); // "2.01"
    console.log(formatter.format(1.345)); // "1.35"

    You can alternatively use the toLocaleString method, which internally will use the Intl API:

    const format = (num, decimals) => num.toLocaleString('en-US', {
       minimumFractionDigits: 2,      
       maximumFractionDigits: 2,
    });
    
    
    console.log(format(2.005)); // "2.01"
    console.log(format(1.345)); // "1.35"

    This API also provides you a wide variety of options to format, like thousand separators, currency symbols, etc.

    0 讨论(0)
  • 2020-11-21 07:01

    Round your decimal value, then use toFixed(x) for your expected digit(s).

    function parseDecimalRoundAndFixed(num,dec){
      var d =  Math.pow(10,dec);
      return (Math.round(num * d) / d).toFixed(dec);
    }
    

    Call

    parseDecimalRoundAndFixed(10.800243929,4) => 10.80 parseDecimalRoundAndFixed(10.807243929,2) => 10.81

    0 讨论(0)
  • 2020-11-21 07:02

    @heridev and I created a small function in jQuery.

    You can try next:

    HTML

    <input type="text" name="one" class="two-digits"><br>
    <input type="text" name="two" class="two-digits">​
    

    jQuery

    // apply the two-digits behaviour to elements with 'two-digits' as their class
    $( function() {
        $('.two-digits').keyup(function(){
            if($(this).val().indexOf('.')!=-1){         
                if($(this).val().split(".")[1].length > 2){                
                    if( isNaN( parseFloat( this.value ) ) ) return;
                    this.value = parseFloat(this.value).toFixed(2);
                }  
             }            
             return this; //for chaining
        });
    });
    

    ​ DEMO ONLINE:

    http://jsfiddle.net/c4Wqn/

    0 讨论(0)
  • 2020-11-21 07:03

    The trouble with floating point values is that they are trying to represent an infinite amount of (continuous) values with a fixed amount of bits. So naturally, there must be some loss in play, and you're going to be bitten with some values.

    When a computer stores 1.275 as a floating point value, it won't actually remember whether it was 1.275 or 1.27499999999999993, or even 1.27500000000000002. These values should give different results after rounding to two decimals, but they won't, since for computer they look exactly the same after storing as floating point values, and there's no way to restore the lost data. Any further calculations will only accumulate such imprecision.

    So, if precision matters, you have to avoid floating point values from the start. The simplest options are to

    • use a devoted library
    • use strings for storing and passing around the values (accompanied by string operations)
    • use integers (e.g. you could be passing around the amount of hundredths of your actual value, e.g. amount in cents instead of amount in dollars)

    For example, when using integers to store the number of hundredths, the function for finding the actual value is quite simple:

    function descale(num, decimals) {
        var hasMinus = num < 0;
        var numString = Math.abs(num).toString();
        var precedingZeroes = '';
        for (var i = numString.length; i <= decimals; i++) {
            precedingZeroes += '0';
        }
        numString = precedingZeroes + numString;
        return (hasMinus ? '-' : '') 
            + numString.substr(0, numString.length-decimals) 
            + '.' 
            + numString.substr(numString.length-decimals);
    }
    
    alert(descale(127, 2));
    

    With strings, you'll need rounding, but it's still manageable:

    function precise_round(num, decimals) {
        var parts = num.split('.');
        var hasMinus = parts.length > 0 && parts[0].length > 0 && parts[0].charAt(0) == '-';
        var integralPart = parts.length == 0 ? '0' : (hasMinus ? parts[0].substr(1) : parts[0]);
        var decimalPart = parts.length > 1 ? parts[1] : '';
        if (decimalPart.length > decimals) {
            var roundOffNumber = decimalPart.charAt(decimals);
            decimalPart = decimalPart.substr(0, decimals);
            if ('56789'.indexOf(roundOffNumber) > -1) {
                var numbers = integralPart + decimalPart;
                var i = numbers.length;
                var trailingZeroes = '';
                var justOneAndTrailingZeroes = true;
                do {
                    i--;
                    var roundedNumber = '1234567890'.charAt(parseInt(numbers.charAt(i)));
                    if (roundedNumber === '0') {
                        trailingZeroes += '0';
                    } else {
                        numbers = numbers.substr(0, i) + roundedNumber + trailingZeroes;
                        justOneAndTrailingZeroes = false;
                        break;
                    }
                } while (i > 0);
                if (justOneAndTrailingZeroes) {
                    numbers = '1' + trailingZeroes;
                }
                integralPart = numbers.substr(0, numbers.length - decimals);
                decimalPart = numbers.substr(numbers.length - decimals);
            }
        } else {
            for (var i = decimalPart.length; i < decimals; i++) {
                decimalPart += '0';
            }
        }
        return (hasMinus ? '-' : '') + integralPart + (decimals > 0 ? '.' + decimalPart : '');
    }
    
    alert(precise_round('1.275', 2));
    alert(precise_round('1.27499999999999993', 2));
    

    Note that this function rounds to nearest, ties away from zero, while IEEE 754 recommends rounding to nearest, ties to even as the default behavior for floating point operations. Such modifications are left as an exercise for the reader :)

    0 讨论(0)
  • 2020-11-21 07:04

    This is an old topic but still top-ranked Google results and the solutions offered share the same floating point decimals issue. Here is the (very generic) function I use, thanks to MDN:

    function round(value, exp) {
      if (typeof exp === 'undefined' || +exp === 0)
        return Math.round(value);
    
      value = +value;
      exp = +exp;
    
      if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
        return NaN;
    
      // Shift
      value = value.toString().split('e');
      value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
    
      // Shift back
      value = value.toString().split('e');
      return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
    }
    

    As we can see, we don't get these issues:

    round(1.275, 2);   // Returns 1.28
    round(1.27499, 2); // Returns 1.27
    

    This genericity also provides some cool stuff:

    round(1234.5678, -2);   // Returns 1200
    round(1.2345678e+2, 2); // Returns 123.46
    round("123.45");        // Returns 123
    

    Now, to answer the OP's question, one has to type:

    round(10.8034, 2).toFixed(2); // Returns "10.80"
    round(10.8, 2).toFixed(2);    // Returns "10.80"
    

    Or, for a more concise, less generic function:

    function round2Fixed(value) {
      value = +value;
    
      if (isNaN(value))
        return NaN;
    
      // Shift
      value = value.toString().split('e');
      value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + 2) : 2)));
    
      // Shift back
      value = value.toString().split('e');
      return (+(value[0] + 'e' + (value[1] ? (+value[1] - 2) : -2))).toFixed(2);
    }
    

    You can call it with:

    round2Fixed(10.8034); // Returns "10.80"
    round2Fixed(10.8);    // Returns "10.80"
    

    Various examples and tests (thanks to @t-j-crowder!):

    function round(value, exp) {
      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));
    }
    function naive(value, exp) {
      if (!exp) {
        return Math.round(value);
      }
      var pow = Math.pow(10, exp);
      return Math.round(value * pow) / pow;
    }
    function test(val, places) {
      subtest(val, places);
      val = typeof val === "string" ? "-" + val : -val;
      subtest(val, places);
    }
    function subtest(val, places) {
      var placesOrZero = places || 0;
      var naiveResult = naive(val, places);
      var roundResult = round(val, places);
      if (placesOrZero >= 0) {
        naiveResult = naiveResult.toFixed(placesOrZero);
        roundResult = roundResult.toFixed(placesOrZero);
      } else {
        naiveResult = naiveResult.toString();
        roundResult = roundResult.toString();
      }
      $("<tr>")
        .append($("<td>").text(JSON.stringify(val)))
        .append($("<td>").text(placesOrZero))
        .append($("<td>").text(naiveResult))
        .append($("<td>").text(roundResult))
        .appendTo("#results");
    }
    test(0.565, 2);
    test(0.575, 2);
    test(0.585, 2);
    test(1.275, 2);
    test(1.27499, 2);
    test(1234.5678, -2);
    test(1.2345678e+2, 2);
    test("123.45");
    test(10.8034, 2);
    test(10.8, 2);
    test(1.005, 2);
    test(1.0005, 2);
    table {
      border-collapse: collapse;
    }
    table, td, th {
      border: 1px solid #ffffd;
    }
    td, th {
      padding: 4px;
    }
    th {
      font-weight: normal;
      font-family: sans-serif;
    }
    td {
      font-family: monospace;
    }
    <table>
      <thead>
        <tr>
          <th>Input</th>
          <th>Places</th>
          <th>Naive</th>
          <th>Thorough</th>
        </tr>
      </thead>
      <tbody id="results">
      </tbody>
    </table>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    0 讨论(0)
  • 2020-11-21 07:04

    Here's a simple one

    function roundFloat(num,dec){
        var d = 1;
        for (var i=0; i<dec; i++){
            d += "0";
        }
        return Math.round(num * d) / d;
    }
    

    Use like alert(roundFloat(1.79209243929,4));

    Jsfiddle

    0 讨论(0)
提交回复
热议问题