PHP7.1 json_encode()Float问题

Gwi*_*d31 67 php precision json php-7.1

这不是一个问题,因为它更多的是意识到.我更新了一个json_encode()用于PHP7.1.1 的应用程序,我看到一个问题,浮动被更改为有时延伸出17位数.根据文档,serialize_precision在编码double值时,PHP 7.1.x开始使用而不是精度.我猜这导致了一个示例值

472.185

成为

472.18500000000006

在那个价值经历之后json_encode().自从我发现以来,我已经恢复到PHP 7.0.16并且我不再有问题了json_encode().我还尝试在恢复到PHP 7.0.16之前更新到PHP 7.1.2.

这个问题背后的原因确实源于PHP - 浮点数精度,但最终所有原因都是因为从精度到serialize_precision使用的变化json_encode().

如果有人知道这个问题的解决方案,我会非常乐意听取推理/修复.

摘自多维数组(之前):

[staticYaxisInfo] => Array
                    (
                        [17] => stdClass Object
                            (
                                [variable_id] => 17
                                [static] => 1
                                [min] => 0
                                [max] => 472.185
                                [locked_static] => 1
                            )

                    )
Run Code Online (Sandbox Code Playgroud)

经过json_encode()......之后

"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },
Run Code Online (Sandbox Code Playgroud)

Mac*_*ity 74

这驱使我坚果一点,直到我终于找到了这个bug指向你这个RFC其说

目前json_encode()使用EG(精度)设置为14.这意味着最多14位用于显示(打印)该数字.IEEE 754 double支持更高的精度和serialize()/ var_export()使用PG(serialize_precision),设置为17默认为更精确.由于json_encode()使用EG(精度),json_encode()即使PHP的浮点数可以保持更精确的浮点值,也可以删除小数部分的低位数并破坏原始值.

而且(强调我的)

这个RFC建议引入一个新设置EG(precision)= - 1和PG(serialize_precision)= - 1,它使用zend_dtoa()的模式0,它使用更好的算法来舍入浮点数(-1用于表示0模式).

简而言之,有一种新方法可以使PHP 7.1 json_encode使用新的和改进的精确引擎.在php.ini中,您需要更改serialize_precision

serialize_precision = -1
Run Code Online (Sandbox Code Playgroud)

您可以使用此命令行验证它是否有效

php -r '$price = ["price" => round("45.99", 2)]; echo json_encode($price);'
Run Code Online (Sandbox Code Playgroud)

你应该得到

{"price":45.99}
Run Code Online (Sandbox Code Playgroud)

  • 请小心“serialize_ precision = -1”。使用 -1 时,此代码 `echo json_encode([528.56 * 100]);` 打印 `[52855.99999999999]` (4认同)
  • @vl.lapikov 不过,这听起来更像是 [一般浮点错误](/sf/ask/41160311/)。这是 [a demo](https://3v4l.org/WbYkM),你可以清楚地看到它不仅仅是一个 `json_encode` 问题 (3认同)

ale*_*lev 25

作为一个插件开发人员,我没有对服务器的php.ini设置的一般访问权限.因此,根据Machavity的回答,我编写了一小段代码,您可以在PHP脚本中使用它.只需将其放在脚本之上,json_encode就会像往常一样继续工作.

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意这一点,因为您的插件可能会更改其余开发人员应用程序的意外设置。但是,海事组织,我不确定该选项会带来多大的破坏性...大声笑 (3认同)

小智 12

我通过将 precision 和 serialize_precision 设置为相同的值(10)解决了这个问题:

ini_set('precision', 10);
ini_set('serialize_precision', 10);
Run Code Online (Sandbox Code Playgroud)

您也可以在 php.ini 中进行设置


tex*_*ate 5

我正在对货币值进行编码,并且将330.46编码到330.4600000000000363797880709171295166015625. 如果您不想或不能更改 PHP 设置并且您事先知道数据的结构,那么有一个非常简单的解决方案对我有用。只需将其强制转换为字符串(以下两者都做同样的事情):

$data['discount'] = (string) $data['discount'];
$data['discount'] = '' . $data['discount'];
Run Code Online (Sandbox Code Playgroud)

对于我的用例,这是一个快速有效的解决方案。请注意,这意味着当您将其从 JSON 解码回来时,它将是一个字符串,因为它将用双引号括起来。