Tofixed() and rounding

Time:2021-12-3

Tofixed() and rounding

I’ve been using ittoFixed()Method to round off floating-point numbers. If only the client displays the data, there is no big problem, but if it involves interacting with the back-end, the accuracy of the data may lead to interface docking failure. Of course, values involving security, such as amount, should not be calculated at the front-end, but at the back-end. In a few cases, if necessary, Its accuracy needs to be repaired

1. Problem scenario

  1. Firstly, we found that in IE browser and other mainstream browsers, due to the storage problem of binary floating-point numbers,toFixed()The behavior of is different, which also shows that the practices of various browser manufacturers are inconsistent.

In IE 11:

0.015.toFixed(2)//   Print result: "0.02"

In chrome:

0.015.toFixed(2)//   Print result: "0.01"

For such problems, some scenarios are unacceptable, and the test may hit a compatibility bug

  1. Second, although we know,toFixed()It’s a kind ofBanker roundingHowever, it does take the order in five instead of the banker‘s rounding. It is also the banker’s rounding. The calculation accuracy of this function is different from the banker’s rounding accuracy of background Java and other languages, which may cause the interface verification failure.

In the front-end browser:

0.025.toFixed(2)//   Print result: "0.03" 0.035.tofixed (2)//   Print result: "0.03"

In the back-end server:

0.025.setScale(2,RoundingMode.HALF_EVEN)//   Print result: "0.02" 0.035.setscale (2, roundingmode. Half_even)//   Print result: "0.04"

In fact, the correct approach should be: the calculation of the relevant amount should be completely handed over to the back-end calculation, and then handed over to the front-end display

2. Repair scheme

The scheme is simple. We just need to rewrite itNumber.prototype.toFixedJust do it.

  1. If we need ordinary rounding:
/*   eslint-disable   no-extend-native  */ //  Avoid eslint. The prototype cannot be modified. The error number.prototype.originaltofixed is reported  =  Number.prototype.toFixed  //  Keep the original method number.prototype.tofixed  =  function(length  =  0)   {  //  0 decimal places are reserved by default    let   result    let   thisNum  =  this.toString()    if   (thisNum.indexOf('.')  === - 1)   thisNum  =  thisNum  + '. 0'    for   (let   i  =  0   i       thisNum  =  thisNum  + ' 0'   }   const   dotIndex  =  thisNum.indexOf('.')    thisNum  =  thisNum.slice(0,   dotIndex)  +  thisNum.slice((dotIndex  +  1),   (dotIndex  +  one  +  length))  + '.' +  thisNum.slice(dotIndex  +  one  +  length)    thisNum  =  Number(thisNum).toString()    const   thisNumList  =  thisNum.split('.')  //  Separating integers from decimals    if   (thisNumList.length  ===  1)   result  =  thisNum.toString()  //  If there are only integers, no processing is required    else   {      if   (Number(thisNumList[1][0])  >=  5)   result  =  (Number(thisNumList[0])  +  1).toString()  //  Five entry      else   result  =  thisNumList[0]  //  Rounding   }   if   (length  ===  0)   return   result    else   {      while   (result.length   1)   {  //  If the number of digits is not enough, fill it with 0        result  = ' 0'  +  result     }     return   result.slice(0,   (result.length  -  length))  + '.' +  result.slice(result.length  -  length)   }}
  1. If we need to use normal banker rounding:
/*   eslint-disable   no-extend-native  */ //  Avoid eslint. The prototype cannot be modified. The error number.prototype.originaltofixed is reported  =  Number.prototype.toFixed  //  Keep the original method number.prototype.tofixed  =  function(length  =  0)   {  //  0 decimal places are reserved by default    let   result    let   thisNum  =  this.toString()    if   (thisNum.indexOf('.')  === - 1)   thisNum  =  thisNum  + '. 0'    for   (let   i  =  0   i       thisNum  =  thisNum  + ' 0'   }   const   dotIndex  =  thisNum.indexOf('.')    thisNum  =  thisNum.slice(0,   dotIndex)  +  thisNum.slice((dotIndex  +  1),   (dotIndex  +  one  +  length))  + '.' +  thisNum.slice(dotIndex  +  one  +  length)    thisNum  =  Number(thisNum).toString()    const   thisNumList  =  thisNum.split('.')  //  Separating integers from decimals    if   (thisNumList.length  ===  1)   result  =  thisNum.toString()  //  If there are only integers, no processing is required    else   {      if   (Number(thisNumList[1][0])  >  5)   result  =  (Number(thisNumList[0])  +  1).toString()  //  Six entry      else   if   (Number(thisNumList[1][0])   5)   result  =  thisNumList[0]  //  Rounding      else   {  //  Judge the situation of 5        if   (thisNumList[1].length  >  1)   result  =  (Number(thisNumList[0])  +  1).toString()  //  Enter if there are digits after 5        else   {          if   (Number(thisNumList[0][thisNumList[0].length  -  1])  %  two  ===  0)   result  =  thisNumList[0]  //  I should give up five years ago          else   result  =  (Number(thisNumList[0])  +  1).toString()  //  Five years ago, I was surprised to enter one       }    }  }   if   (length  ===  0)   return   result    else   {      while   (result.length   1)   {  //  If the number of digits is not enough, fill it with 0        result  = ' 0'  +  result     }     return   result.slice(0,   (result.length  -  length))  + '.' +  result.slice(result.length  -  length)   }}

This unifies the rounding accuracy and can be selected according to the needs of the background.