修复 PHP7.1、7.2beta 中 JSON_encode () 处理 float/double 型数值时溢出的问题

在 PHP7.1、7.2beta 中,使用 json_encode() 函数处理 float/double 型数值时会出现溢出。
对此,我参考网上的方法写了一个 package ,欢迎使用 ^_^ 。

感谢 @ElfSundae 解答,这个问题确实是由于 php.iniserialize_precision 项中配置不当引起的,是我理解错了。
PHP 官方 WIKI 在此:https://wiki.php.net/rfc/precise_float_val...

示例

  >>> $a = 0.1 + 0.7
  => 0.8
  >>> printf('%.20f', $a)
  => 0.79999999999999993339
  >>> json_encode($a)
  => "0.7999999999999999"
  >>> \YaJson::encode($a)
  => "0.8"

用法

  1. 修复精度并进行 json_encode

    $data = [
      'a' => 0.1 + 0.7,
      'b' => ['string1', 'string2'],
    ];
    
    \YaJson::encode($data);
  2. 只获取修复后的数据,不进行 json_encode

    $data = [
      'a' => 0.1 + 0.7,
      'b' => ['string1', 'string2'],
    ];
    
    \YaJson::prepare($data);

安装

  1. 安装包文件

    composer require "seekerliu/laravel-another-json:dev-master"

配置

Laravel 5.5

Laravel 5.5 安装新包后会默认执行 @php artisan package:discover 命令,所以可以不进行下面的操作。

  1. 注册 ServiceProviderFacade:

    php artisan package:discover
  2. 如需修改默认循环深度、精度位数,则创建配置文件:

    php artisan vendor:publish

Laravel 5.4 及以下

  1. 注册 ServiceProviderFacade:

    'providers' => [
      //...
    
      Seekerliu\YaJson\ServiceProvider::class,
    ],
    
    'aliases' => [
      //...
    
      'YaJson' => Seekerliu\YaJson\Facade::class,
    ],
  2. 如需修改默认循环深度、精度位数,则创建配置文件:

    php artisan vendor:publish --provider="Seekerliu\YaJson\ServiceProvider"

Github: https://github.com/seekerliu/laravel-anoth...
感谢博主提供的思路: http://www.itread01.com/articles/148977474...

本作品采用《CC 协议》,转载必须注明作者和本文链接
原创。 所有 Laravel 文章均已收录至 Github laravel-tips 项目。
Ίκαρος
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 13
Ίκαρος

@mingyun 谢谢~ 我又测试了一下,确实只有 7.1 和 7.2 有这个问题,已修改正文描述~ :thumbsup:

6年前 评论
Ίκαρος

@Rekkles :thumbsup::thumbsup::thumbsup:

6年前 评论

把 serialize_precision 修改成别的值就好了啊, 最好在代码里改 ini_set('serialize_precision', 14);

把你的演示修改了下: https://3v4l.org/T1ZmY

6年前 评论

JSON 目的是为数据交换,各个语言或系统中的「对象」表示不一样,JSON 就是中间桥梁。
把你的 json_encode 后看起来错误的数据放到别的系统上执行 json_decode 如果得到的值是期望的就行。

>>> $a = 0.1 + 0.7
=> 0.8
>>> $foo = json_encode($a)
=> "0.7999999999999999"
>>> json_decode($foo)
=> 0.8
6年前 评论
Ίκαρος

@ElfSundae 谢谢~原来官方说明在这里,找了很久都没找到 ^_^。
刚刚测试了一下,如果不把 json_encode 看起来错误的数据处理一下,放在别的系统是有可能得到错误的结果的。
如果别的系统配置的精度设置很高,就会得到 0.7999999999999 这样的结果,而不是 0.8。例如: https://3v4l.org/0IoWn
而精度设置稍低,又正常了 https://3v4l.org/gJqSE

6年前 评论
Ίκαρος

@ElfSundae 其实我的例子,最好的解决方法是用 bcmath 哈哈

6年前 评论

嗯 浮点精度在每个系统的处理有差异, PHP 使用的是 IEEE 754 ,PHP 7.1 的 json_encode 使用了 serialize_precision (默认 -1) 是把浮点精度提高了。要跟 7.1 之前的结果一样的话最便捷的就是修改 serialize_precision 为 precision 值,14 是默认值。但是最好的方法是永远不要相信浮点的精度,这在很多系统上都有说明,比如 PHP 的警告:http://php.net/manual/zh/language.types.fl...

6年前 评论
Ίκαρος

@ElfSundae 说来这个问题也不算 bug 了。。原本指望 7.2 能改回去 :smirk:
我还是回去补习下浮点数在内存的表示。。

6年前 评论

厉害,大佬

6年前 评论

个人认为 Json只是一种数据传输格式 并不应该参与运算处理之类的逻辑
浮点数运算 应该手动处理 比如bcmath函数或者类型转换 可能稍微繁琐点 但不会出现由Json编码导致的错误异常
Json错误异常(json_last_error())目前已经有10种(PHP7.3为止)
所以应该养成一种习惯 先做好数据处理工作 再Json编码 这样由Json编码导致的异常可尽量避免

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!