有人说toFixed不是四舍五入而是采用了银行家舍入法?我有点不信

lxf2023-05-15 01:21:35

起因

首页给我推荐了[这篇文章](别再回答面试官,toFixed采用的是四舍五入啦! - 编程 ()),文章里说toFixed不是用的四舍五入,而是四舍六入五考虑,即银行家舍入法。

但是我总感觉有点不对劲。有兴趣的朋友可以先点开上文看下,下文会解释toFixed到底用的什么。

四舍五入就不说了,先来看看这个银行家舍入法

原文的解释是:

  • 情况1:保留位数的后一位如果小于5,则舍去。

  • 情况2:保留位数的后一位如果大于5,则进。

  • 情况3:保留位数的后一位如果是5

    • 情况a:如果5后面仍有非0数,则直接进1。
    • 情况b:若5后面不再有非0数,要根据尾数“5”的前一位决定:
      • 情况x:如果前一位是奇数则进。
      • 情况y:如果是偶数则舍去。

例子

5.214.toFixed(2)
'5.21'  // 情况1
5.216.toFixed(2)
'5.22'  // 情况2
5.2254.toFixed(2)
'5.23'  // 情况a
5.215.toFixed(2)
'5.21'  // 情况x,但结果不对,5前一位是1但并未进1
5.225.toFixed(2)
'5.22'  // 情况y

对此原文的解释为

5.215.toPrecision(17)
'5.2149999999999999' //计算机浮点数精度问题

计算机浮点数存在精度问题,很多文章讲过这个IEEE 754-2019。不多说了。 原文上5.215底层竟然是5.214999……,那么此时按照奇进偶舍的规则,第三位4小于5直接舍弃,就成了5.21。实则匹配到情况1.

看似挺合理的,直到1.125的出现,发现不对劲了。

1.125.toPrecision(17)
'1.1250000000000000'
1.125.toFixed(2) 
'1.13'  // 情况y,5后面没有数,5前面是偶数,但是并没有舍去,而是进1.

0.125这种小数,2 的-3次方 ,是可以通过二进制完美表示的,不存在精度问题。 符合银行家舍入法情况y,但是结果却不对,这是不是说明toFixed并没有采用符合银行家舍入法?。

到底toFixed怎么操作的呢?

翻开es的最新规范(ECMAScript® 2024 Language Specification (tc39.es))的伪代码看一下就知道了。

有人说toFixed不是四舍五入而是采用了银行家舍入法?我有点不信

翻译一下关键步骤操作: step1-2: x 是原数字,f为参数,即要保留的位数

step2-6: 处理边界和错误

step7:将x转为R(x),其实就是以计算机内浮点数表示来看待x,这时候就会有精度问题了。这一步很关键,需要用于后面的step11的计算。

step8-10:不说了

step11:找到一个整数n,使得n / 10f - x 的结果尽可能的接近0.如果有2个n满足条件,选择更大的数作为n.最关键的一步就是找这个整数n,后面的就是把小数点放在n里面了,不多解释了。

拿1.125 .toFixed(2)来走一下上面的流程:

  1. x=1.125,f=2;
  2. 经过step7,可以认为x = 1.12500000000000...;
  3. 寻找整数n,使n/100-1.1250000000 最小,尽可能的接近0,有两个n会满足条件,一个是112,一个是113。取大的那个数,所以n为113,小数点放进去,最后返回1.13字符串

完美!1.125 .toFixed(2) === '1.13'

再看5.215,经过step7,x= 5.2149999999999999,再到step11,n只能找到521。所以5.215.toFixed(2) === '5.21'

总结

根据最新的规范,toFixed 采用的方法既不是四舍五入也不是银行家舍入。而是一个最近原则的的算法规则(同近取大)。

当然,也不能说原文错误,因为可能在老版本的规范或者浏览器里面他可能是采用银行家舍入法后者四舍五入法的。

所以前提条件很重要。

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!