javascripttofixed

Solution for Javascript toFixed and math.round wrong issue


Is there a way to proper round the number 35.855 to have two decimals in javascript? Trying to round it has different solutions based on the environment (35.85 in Chrome, 35.86 in IE9).

I was looking for a solution at which javascript does wrong rounding for the decimals (rounding .5 values down instead up) and haven't found a solution that covered all the values that I had. They behaved other in different environments / browsers.

console.log(Math.round(35.855*100)/100);
// result: 35.85 (IE9 : 35.86, Chrome: 35.85 ???)
console.log((35.855).toFixed(2));
// result: 35.85


Solution

  • Make rounding of 1 character after the decimal part:

    const round = (num, digits = 2) => {
      const str = num.toString(), idx = str.indexOf('.');
      if (idx < 0) return num;
      const left = str[idx + 1 + digits]; // round this char
      if (!left) return num;
      return parseFloat(str.slice(0, idx + 1 + digits)) + 
          (left >= 5) * (str[0] === '-' ? -1 : 1) / 10 ** digits;
    }
    [35.855, 35.8549999, [-35.8549999, 3], 35.895, 35.8, 35].forEach(num => 
      console.log(JSON.stringify(num), '->', round(...[].concat(num))));

    And a benchmark:

    Cycles: 1000000 / Chrome/117
    ------------------------------------------------------------
    Alexander slice       151/min  1.0x  163  166  159  151  169
    Sercan Samet Savran   845/min  5.6x  846  845  846  851  859
    ------------------------------------------------------------
    https://github.com/silentmantra/benchmark
    

    <script benchmark data-count="1000000">
    
    // @benchmark Alexander slice
    const round = (num, digits = 2) => {
      const str = num.toString();
      const idx = str.indexOf('.');
      if(idx < 0){
        return num;
      }
      const left = str[idx + 1 + digits];
      return left ? parseFloat(str.slice(0, idx + 1 + digits)) +(left >= 5) * (str[0] === '-' ? -1 : 1)/ 10 ** digits : num;
    }
    // @run
    
    round(35.855);
    round(35.8549999);
    round(35.8549999, 3);
    round(35.85);
    round(35.8);
    round(35);
    
    // @benchmark Sercan Samet Savran
    
    function roundToTwoDecimal(myNumber){
        let myStrNumber = myNumber.toString();
        // checking our number has decimals and if enough decimal points are present 
        if(myStrNumber.split(".").length > 1 && myStrNumber.split(".")[1].length > 2){
            // doubling the last decimal e.g. 35.855 is now = 35.8555 and parsing back to float  
            myStrNumber += myStrNumber.substring(myStrNumber.length-1);
            myNumber = parseFloat(myStrNumber);
        } // no additional decimal needed here.
        // returning the myNumber*100 = 3585.55 ---round--> 3586 / 100 = 35.86 
        return Math.round(myNumber*100)/100;
    }
    
    // @run
    
    roundToTwoDecimal(35.855);
    roundToTwoDecimal(35.8549999);
    roundToTwoDecimal(35.8549999, 3);
    roundToTwoDecimal(35.85);
    roundToTwoDecimal(35.8);
    roundToTwoDecimal(35);
    
    
    </script>
    <script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>