PHP7.1 json_encode()Float问题

时间:2017-03-23 16:11:14

标签: php json precision php-7.1

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

的示例值
  

472.185

成为

  

472.18500000000006

在该值经过json_encode()之后。自从我的发现以来,我已经恢复到PHP 7.0.16并且我不再遇到json_encode()的问题。我还尝试在恢复到PHP 7.0.16之前更新到PHP 7.1.2。

这个问题背后的原因确实源于PHP - Floating Number Precision,但最终所有原因都是因为json_encode()中从精确度到serialize_precision用法的变化。

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

摘录自多维数组(之前):

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

                    )

并在经过json_encode() ...

之后
"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },

12 个答案:

答案 0 :(得分:64)

这让我疯了一会儿,直到我终于发现this bug指向你this RFC

  

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

并且(强调我的)

  

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

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

serialize_precision

您可以验证它是否适用于此命令行

serialize_precision = -1

你应该

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

答案 1 :(得分:16)

作为插件开发人员,我无法对服务器的php.ini设置进行常规访问。因此,根据Machavity的回答,我编写了一小段代码,您可以在PHP脚本中使用它。只需将其置于脚本之上,json_encode将继续照常工作。

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}

答案 2 :(得分:3)

我有同样的问题,但只有serialize_precision = -1没有解决问题。我不得不再做一步,将精度值从14更新为17(因为它是在我的PHP7.0 ini文件中设置的)。显然,更改该数字的值会更改计算浮点数的值。

答案 3 :(得分:3)

其他解决方案对我没有用。这是我在代码执行开始时必须添加的内容:

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}

答案 4 :(得分:2)

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

ini_set('precision', 10);
ini_set('serialize_precision', 10);

您也可以在php.ini中设置它

答案 5 :(得分:2)

在 php 7.2.32 上,解决方案是在 php.ini 中设置:

precision=10
serialize_precision=10

答案 6 :(得分:0)

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

$data['discount'] = (string) $data['discount'];
$data['discount'] = '' . $data['discount'];

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

答案 7 :(得分:0)

您可以将[max] => 472.185从浮点数更改为json_encode()之前的字符串([max] =>'472.185')。由于json仍然是字符串,因此将您的float值转换为json_encode()之前的字符串将保持您想要的值。

答案 8 :(得分:0)

对我来说,问题是当JSON_NUMERIC_CHECK作为json_encode()的第二个参数传递时,该类型将所有数字强制转换为int(不仅是整数)

答案 9 :(得分:0)

使用number_format将其存储为具有所需精确度的字符串,然后使用json_encode选项将其JSON_NUMERIC_CHECK存储:

$foo = array('max' => number_format(472.185, 3, '.', ''));
print_r(json_encode($foo, JSON_NUMERIC_CHECK));

您得到:

{"max": 472.185}

请注意,这会将源对象中的所有数字字符串转换为结果JSON中的数字。

答案 10 :(得分:0)

$val1 = 5.5;
$val2 = (1.055 - 1) * 100;
$val3 = (float)(string) ((1.055 - 1) * 100);
var_dump(json_encode(['val1' => $val1, 'val2' => $val2, 'val3' => $val3]));
{
  "val1": 5.5,
  "val2": 5.499999999999994,
  "val3": 5.5
}

答案 11 :(得分:0)

serialize_precisionserialize_precision设置为不同的值时,似乎会出现问题。在我的情况下分别为14和17。将它们都设置为14可解决此问题,将serialize_precision设置为-1也可以解决此问题。

{{1}} was changed to -1 as of PHP 7.1.0的默认值,这意味着“将使用用于舍入此类数字的增强算法”。但是,如果仍然遇到此问题,则可能是因为您有一个来自先前版本的PHP配置文件。 (也许升级后保留了配置文件?)

要考虑的另一件事是,在您的情况下完全使用浮点值是否有意义。使用包含数字的字符串值来确保始终在JSON中始终保留正确的小数位数可能没有意义。