V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mentalidade
V2EX  ›  程序员

js 解析 json 精度丢失怎么处理?

  •  
  •   mentalidade · 2018-10-31 17:18:46 +08:00 · 7474 次点击
    这是一个创建于 2222 天前的主题,其中的信息可能已经有所发展或是发生改变。

    后端保留两位小数,前端显示却丢失了,查了下,是 js 解析 float 精度丢失。不想在后端改成 string,怎么让前端正确显示。

    后端 php 是 float 保留两位小数,转为 json。

        ["money"]=>
        array(3) {
          ["maket_total"]=>
          float(16794.48)
          ["voucher_total"]=>
          string(6) "744.00"
          ["favorable_total"]=>
          float(509.46)
        }
    

    前端接收却是:"money":{"maket_total":16794.48,"voucher_total":"744.00","favorable_total":509.46000000000004}},

    第 1 条附言  ·  2018-11-01 09:43:20 +08:00
    array(4) {
      ["data"]=>
      array(2) {
        ["saled"]=>
        object(Illuminate\Database\Eloquent\Collection)#1235 (1) {
          ["items":protected]=>
          array(0) {
          }
        }
        ["money"]=>
        array(3) {
          ["maket_total"]=>
          float(16794.48)
          ["voucher_total"]=>
          string(6) "744.00"
          ["favorable_total"]=>
          float(509.46)
        }
      }
      ["status"]=>
      int(0)
      ["msg"]=>
      string(12) "操作成功"
      ["total"]=>
      int(0)
    }
    
    php的json_encode()之后:
    string(170) "{"data":{"saled":[],"money":{"maket_total":16794.48,"voucher_total":"744.00","favorable_total":509.46000000000004}},"status":0,"msg":"\u64cd\u4f5c\u6210\u529f","total":0}"
    

    然后json_encode()就丢失精度了

    第 2 条附言  ·  2018-11-01 09:44:52 +08:00
    这是 laravel 框架里的,自己单独写一个文件却没有重现这个问题
    28 条回复    2022-11-05 21:36:44 +08:00
    TommyLemon
        1
    TommyLemon  
       2018-10-31 17:28:30 +08:00   ❤️ 1
    也保留两位小数
    TomatoYuyuko
        2
    TomatoYuyuko  
       2018-10-31 17:30:28 +08:00   ❤️ 1
    不了解 php 怎么做的,我这里大概思路是用 string 类型传送浮点数,然后前端再做处理。
    mentalidade
        3
    mentalidade  
    OP
       2018-10-31 17:33:04 +08:00
    @TommyLemon #1
    @TomatoYuyuko #2 我用的 toFixed(2) 解决了
    HanMeiM
        4
    HanMeiM  
       2018-10-31 17:42:58 +08:00   ❤️ 1
    一般要么存整数,要么传 string
    lance7in
        5
    lance7in  
       2018-10-31 17:51:24 +08:00   ❤️ 1
    string
    mentalidade
        6
    mentalidade  
    OP
       2018-10-31 17:51:51 +08:00
    @HanMeiM #4
    @TomatoYuyuko #2 青门你们也是后端吗?因为是钱得数值,保留两位小数的浮点数。必须要转换为 string 吗?
    micean
        7
    micean  
       2018-10-31 17:54:21 +08:00   ❤️ 1
    钱的话谨慎用 toFixed
    HanMeiM
        8
    HanMeiM  
       2018-10-31 17:55:20 +08:00   ❤️ 1
    @mentalidade 是后端啊,这里没说是钱啊。其次我们的钱单位为厘,分的更小一位
    SakuraKuma
        9
    SakuraKuma  
       2018-10-31 18:00:17 +08:00   ❤️ 1
    钱的话建议传 /存储都是整数不要小数。
    小数只是显示用。

    这浮点数精度无解的
    MrJeff
        10
    MrJeff  
       2018-10-31 18:00:58 +08:00   ❤️ 1
    用 string 吧 省事
    westoy
        11
    westoy  
       2018-10-31 18:01:44 +08:00
    小额可以传 long, 末两位做小数, 注意控制溢出

    或者传字符窜, 用 decimal 或者 gmp

    既然不想在后端改, 那就无解了

    浮点一时爽, 会计对账小一半订单有个几毛几分的误差就 happy 了
    TomatoYuyuko
        12
    TomatoYuyuko  
       2018-10-31 18:08:27 +08:00   ❤️ 1
    @mentalidade 前端,一般来说前端不处理数据,只负责呈现,没有特别需求保留 2 位小数就好了。
    当然数据库你肯定要存精确的,你可以直接在服务端把数字精确处理后再给前端。或者 string 之后给前端处理。
    js 这类语言对浮点数处理会有误差,尽可能不要让 js 处理小数运算。
    atcdef
        13
    atcdef  
       2018-10-31 19:28:29 +08:00   ❤️ 1
    这个无解的,要么前端格式化一下再显示,要么后端传格式化好的字符串来。我一般都是传格式化好的字符串到前端
    8e47e42
        14
    8e47e42  
       2018-10-31 19:37:46 +08:00   ❤️ 1
    一般比较容易的是直接传 string,当然也可以选择成本扩大变成 bigint,当然很多库默认都不支持 bigint,然后你懂的,GG,到最后还是变成了传 string 最靠谱。
    sxlzll
        15
    sxlzll  
       2018-10-31 22:15:12 +08:00
    如果是钱,单位可以改成分
    其他的 float,确实比较稳妥的做法是两个字段,data, data_str
    dd112389
        16
    dd112389  
       2018-11-01 01:00:47 +08:00   ❤️ 3
    这里有个坑,前些天才遇到过.
    你可以看一下 php 的 json_encode 函数的说明, 里面有写 php.ini 中的一个设置 serialize_precision 会导致 json_encode 的精度丢失问题.
    将这个设置改为 16 以下应该就不会了.
    ini_set('serialize_precision', 15);
    justyue
        17
    justyue  
       2018-11-01 08:37:13 +08:00 via iPhone   ❤️ 1
    我司,钱都是转成 string 传的,安全方便
    mentalidade
        18
    mentalidade  
    OP
       2018-11-01 09:24:47 +08:00
    @dd112389 #16 是的,我发现是 json_encode()的问题,但是上面的实际的例子,maket_total 这个值就没事,favorable_total 却发生了精度丢失,明明两个都是保留两位小数的 float ?求解
    weixiangzhe
        19
    weixiangzhe  
       2018-11-01 09:24:56 +08:00
    我司都是整数
    lukunlin
        20
    lukunlin  
       2018-11-01 09:51:36 +08:00
    .toFixed(2)一下就好了啊~
    BBCCBB
        21
    BBCCBB  
       2018-11-01 10:18:32 +08:00
    long 类型 超过 53 还是 54, js 解析也会出问题的, 还是用 string 吧.
    BBCCBB
        22
    BBCCBB  
       2018-11-01 10:18:55 +08:00
    53, 54 位
    promise2mm
        23
    promise2mm  
       2018-11-01 11:13:33 +08:00
    http://www.css88.com/archives/7340#more-7340
    这里有大佬的分析,我司也遇到过
    shuang
        24
    shuang  
       2018-11-01 11:27:31 +08:00
    0.495.toFixed(2)
    "0.49"
    kran
        25
    kran  
       2018-11-01 11:29:56 +08:00 via iPhone
    乘上 10^n 再编码成 json
    zjsxwc
        26
    zjsxwc  
       2018-11-01 11:32:00 +08:00
    php7.1.x 特定小版本的问题,

    加一句
    ```
    ini_set('serialize_precision', -1);
    ```
    就行

    http://php-symfony.uk/json_encode-producing-unexpected-float-values-in-php-7-1/
    msg7086
        27
    msg7086  
       2018-11-01 11:37:06 +08:00
    钱用浮点数这种近似表示法是作大死。只要是近似表示,就会有误差,再怎么都会有。
    Jobing
        28
    Jobing  
       2022-11-05 21:36:44 +08:00
    可以用这个开源库解决: https://github.com/jobinben/json-bn
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1056 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 23:24 · PVG 07:24 · LAX 15:24 · JFK 18:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.