V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
a494836960
V2EX  ›  JavaScript

JS 的 toFixed 方法到底是怎么取值的

  •  
  •   a494836960 · 2019-06-05 11:35:17 +08:00 · 5507 次点击
    这是一个创建于 2000 天前的主题,其中的信息可能已经有所发展或是发生改变。

    文档上说的是 四舍五入 为什么 52.635 就是 52.63 呢

    52.635.toFixed(2)  // "52.63"
    47.365.toFixed(2)  // "47.37"
    
    28 条回复    2019-09-26 14:11:39 +08:00
    kile
        1
    kile  
       2019-06-05 11:47:04 +08:00   ❤️ 3
    入了就入了,舍了就舍了,想入就入,想舍就舍

    我是 JS,我为我自己带盐
    liuy1994g
        2
    liuy1994g  
       2019-06-05 11:51:25 +08:00 via Android
    偶进奇不进?
    luzemin
        3
    luzemin  
       2019-06-05 11:55:31 +08:00
    每个语言都有这个问题,C#为例,也是奇进偶舍。https://www.cnblogs.com/lztkdr/p/MathRoundToEven.html
    huawin03
        4
    huawin03  
       2019-06-05 11:56:31 +08:00
    按道理应该是四舍五入,这个可能是浮点数精度问题
    Caballarii
        5
    Caballarii  
       2019-06-05 11:56:39 +08:00
    重写 Number.prototype.toFixed
    xiangyuecn
        6
    xiangyuecn  
       2019-06-05 11:58:21 +08:00
    Math.round(52.635*100)/100

    (52.635+0.000001).toFixed(2)

    坑死人不偿命

    9007199254740992+1 = 多少

    9007199254740995+1 = 多少
    wakiki
        7
    wakiki  
       2019-06-05 12:28:38 +08:00 via iPhone
    ECMAScript 的 Spec 上没有说是四舍五入啊🤷‍♂️
    runze
        8
    runze  
       2019-06-05 12:34:21 +08:00
    Number.prototype.toFixed 并没有说是四舍五入,想要四舍五入请用 Math.round
    zhy0216
        9
    zhy0216  
       2019-06-05 12:38:04 +08:00 via Android
    学习了 今天刚用到这个函数 竟然有这个坑。。。
    wakiki
        10
    wakiki  
       2019-06-05 12:44:03 +08:00 via iPhone   ❤️ 1
    Let n be an integer for which the exact mathematical value of n ÷ 10f - x is as close to zero as possible. If there are two such n, pick the larger n.
    主要因为是 52.635 存为 52.634998..,对应的 n 是 5263,所以得到 53.63 ,47.365 存为 47.365001678....,所以 n 是 4737,得到 47.37
    helllllloworld
        11
    helllllloworld  
       2019-06-05 13:02:23 +08:00
    这种事情,当然是去看 Spec 最靠谱了啊
    dangyuluo
        12
    dangyuluo  
       2019-06-05 13:11:02 +08:00   ❤️ 4
    52.635 的 IEEE754 实值为 52.634998321533203125,自然四舍五入成 52.63
    a494836960
        13
    a494836960  
    OP
       2019-06-05 13:22:08 +08:00
    @wakiki 说得对。。。 我之前看的是 w3school
    jucelin
        14
    jucelin  
       2019-06-05 13:33:47 +08:00   ❤️ 1
    使用的是银行家舍入规则:
    四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

    https://www.jb51.net/article/126804.htm
    MinonHeart
        15
    MinonHeart  
       2019-06-05 14:37:50 +08:00   ❤️ 1
    Math.fround(52.635); // 52.6349983215332
    Math.fround(47.365); // 47.3650016784668
    marcong95
        16
    marcong95  
       2019-06-05 14:40:33 +08:00
    这不是坑,Banker Rounding 了解一下。需要四舍五入请使用(x, n) => Math.floor((x * 10 ** n) + 0.5) / (10 ** n)

    楼上说坑的,请重温 JS 语法甚至计算机编程基础……基础不打好别怪语言,各种语言的取整算法都有这个问题。
    xiangyuecn
        17
    xiangyuecn  
       2019-06-05 15:15:54 +08:00
    @marcong95 就算这是是标准。但需要特定值才能触发非预期的结果就是坑呀,我来给他安一个自定义名字:标准坑😄

    如果没有被坑过,我相信像我这种刚入门的小白哪会留意这些标准事实。只会表示明明测试的好好的,就是不知道为什么线上偶尔会产生不一致的数据,最后发现是 toFixed 的锅:52.635.toFixed(2) != "52.64"

    埋雷好方法:需要特定值才能触发非预期的结果,测试过程难发现,有利提升自己存在价值😜
    66beta
        18
    66beta  
       2019-06-05 15:19:51 +08:00 via Android
    讨论了这么多规范,难道不该考虑下业务上是希望四舍五入吗?
    version
        19
    version  
       2019-06-05 15:46:22 +08:00
    js 计算金额问题.就需要看业务需要了.看老板的取舍了
    当要对接其他语言的时候也需要相互沟通.按分还是按元.按 int 还是按浮点来传值. 来换算再计算还是其它.支付都对接第三方.基本基础可以.不会存在太大问题..
    而且金额最好是多转转 number 不然写着写着就是 字符串相加了.那就是出大事了.还有边界问题限制下就好.不然测试哪天给你测试的金额是恐怖的
    四舍五入或者去尾.可以用 require('lodash') 这个库
    wakiki
        20
    wakiki  
       2019-06-05 16:01:12 +08:00   ❤️ 11
    别被上面说的“ Banker Rounding ”误导,Spec 里也没说用着这个,只是输出结果看起来类似,面试答了这个就尴尬了。
    Spec 里说的是:

    Let n be an integer for which the exact mathematical value of n ÷ 10f - x is as close to zero as possible. If there are two such n, pick the larger n

    对于 52.635:
    5263/100 - 52.635 = -0.0049999999999954525
    5264/100 - 52.635 = 0.005000000000002558
    最接近 0 的是 5263/100 - 52.635 的结果,所以结果是 52.63
    根本原因是数字表示方式的精度问题啊啊啊啊啊啊啊啊啊啊
    xiangyuecn
        21
    xiangyuecn  
       2019-06-05 16:13:38 +08:00
    @wakiki #20 完美的解释了: (52.635+0.00000000000001).toFixed(2) 是正确的,在多一个 0 就不确了。
    cifermail
        22
    cifermail  
       2019-06-05 16:26:40 +08:00
    这是因为精度问题,不是真正的四舍五入,解决 toFixed 四舍五入陷阱, https://www.boatsky.com/blog/32
    xieweizhi007
        23
    xieweizhi007  
       2019-06-05 16:28:15 +08:00
    @wakiki 好认真的回答, 赞一个。
    20498860
        24
    20498860  
       2019-06-05 17:57:54 +08:00
    看到 3 楼我去研究了半个小时 toFixed 和银行家舍入。。回来看到 20 楼发现不是银行家舍入。。我佛了
    MinonHeart
        25
    MinonHeart  
       2019-06-05 18:39:49 +08:00
    @xiangyuecn js 浮点数不能用 == 比较,通常是差值和 Number.EPSILON 比较,高精度应该用 decimal,BigInt
    Fule
        26
    Fule  
       2019-06-06 08:26:17 +08:00
    本来想说 JS 奇葩,但是 C# 里下面的代码得到的结果和 JS 的一样…… 🤣

    ```
    float a = 52.635f;
    double b = Math.Round(a,2);
    Console.WriteLine(b);
    ```
    flowfire
        27
    flowfire  
       2019-07-30 11:04:25 +08:00
    四舍六入五成双
    leopoldgod
        28
    leopoldgod  
       2019-09-26 14:11:39 +08:00
    网上很多人人云亦云,说 toFixed 是银行家舍入,其实并不是,自己试一下就知道了。五前为偶要舍去的反例:(10.1250).toFixed(2)//10.13 ,五前为奇要进一的反例:(10.2150).toFixed(2)//10.21 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3405 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:55 · PVG 19:55 · LAX 03:55 · JFK 06:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.