php PHP7.1 json_encode() 浮动问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42981409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 02:44:08  来源:igfitidea点击:

PHP7.1 json_encode() Float Issue

phpjsonprecisionphp-7.1

提问by Gwi7d31

This isn't a question as it is more of a be aware. I updated an application that uses json_encode()to PHP7.1.1 and I was seeing an issue with floats being changed to sometimes extend out 17 digits. According to documentation, PHP 7.1.x started to use serialize_precisioninstead of precision when encoding double values. I'm guessing this caused an example value of

这不是一个问题,因为它更像是一个注意事项。我更新了一个使用json_encode()PHP7.1.1的应用程序,我看到浮点数被更改为有时扩展到 17 位数字的问题。根据文档,PHP 7.1.xserialize_precision在编码双精度值时开始使用精度代替精度。我猜这导致了一个示例值

472.185

472.185

to become

成为

472.18500000000006

472.18500000000006

after that value went through json_encode(). Since my discovery, I have reverted back to PHP 7.0.16 and I no longer have the issue with json_encode(). I also tried to update to PHP 7.1.2 before reverting back to PHP 7.0.16.

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

The reasoning behind this question does stem from PHP - Floating Number Precision, however the end all reason for this is because of the change from precision to serialize_precision usage in json_encode().

这个问题背后的原因确实源于PHP-Floating Number Precision,但最终所有原因都是因为在json_encode().

If anyone does know of a solution to this problem, I'd be more than happy to listen in on the reasoning/fix.

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

Excerpt from multidimensional array (before):

多维数组摘录(之前):

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

                    )

and after going through json_encode()...

经过json_encode()...

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

回答by Machavity

This drove me nuts for a bit until I finally found this bugwhich points you to this RFCwhich says

这让我有点发疯,直到我终于找到了这个错误,它指向这个 RFC,它说

Currently json_encode()uses EG(precision) which is set to 14. That means that 14 digits at most are used for displaying (printing) the number. IEEE 754 double supports higher precision and serialize()/var_export()uses PG(serialize_precision) which set to 17 be default to be more precise. Since json_encode()uses EG(precision), json_encode()removes lower digits of fraction parts and destroys original value even if PHP's float could hold more precise float value.

目前json_encode()使用的EG(precision) 设置为14。也就是说最多使用14 位数字来显示(打印)数字。IEEE 754 double 支持更高的精度和serialize()/var_export()使用 PG(serialize_precision) 设置为 17 默认为更精确。由于json_encode()使用 EG(precision),json_encode()即使 PHP 的 float 可以保存更精确的 float 值,也会删除小数部分的低位数字并破坏原始值。

And (emphasis mine)

和(强调我的)

This RFC proposes to introduce a new setting EG(precision)=-1 and PG(serialize_precision)=-1 that uses zend_dtoa()'s mode 0 which uses better algorigthm for rounding float numbers (-1 is used to indicate 0 mode).

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

In short, there's a new way to make PHP 7.1 json_encodeuse the new and improved precision engine. In php.iniyou need to change serialize_precisionto

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

serialize_precision = -1

You can verify it works with this command line

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

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

You should get

你应该得到

{"price":45.99}

回答by alev

As a plugin developer I don't have general access to the php.ini settings of a server. So, based on Machavity's answer I wrote this small piece of code that you can use in your PHP script. Simply put it on top of the script and json_encode will keep working as usual.

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

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

In some cases it is necessary to set one more variable. I am adding this as a second solution because I am not sure if the second solution works fine in all cases where the first solution has proven to work.

在某些情况下,需要再设置一个变量。我将其添加为第二个解决方案,因为我不确定第二个解决方案是否在第一个解决方案已被证明有效的所有情况下都能正常工作。

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

回答by Alin Pop

I had the same problem but only serialize_precision = -1 did not solve the problem. I had to do one more step, to update the value of precision from 14 to 17 (as it was set on my PHP7.0 ini file). Apparently, changing the value of that number changes the value of the computed float.

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

回答by Mike P. Sinn

The other solutions didn't work for me. Here's what I had to add at the beginning of my code execution:

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

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

回答by texelate

I was encoding monetary values and had things like 330.46encoding to 330.4600000000000363797880709171295166015625. If you don't wish to, or can't, change the PHP settings and you know the structure of the data in advance there is a very simple solution that worked for me. Simply cast it to a string (both the following do the same thing):

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

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

For my use case this was a quick and effective solution. Just note that this means when you decode it back from JSON it will be a string since it'll be wrapped in double quotes.

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

回答by whatever_sa

I solved this by setting both precision and serialize_precision to the same value (10):

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

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

You can also set this in your php.ini

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

回答by pasqal

Store it as a string with the exact precision that you need by using number_format, then json_encodeit using the JSON_NUMERIC_CHECKoption:

通过使用number_formatjson_encode其存储为具有您需要的精确精度的字符串,然后使用JSON_NUMERIC_CHECK选项:

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

You get:

你得到:

{"max": 472.185}

Note that this will get ALL numeric strings in your source object to be encoded as numbers in the resulting JSON.

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

回答by B.Asselin

$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
}

回答by Acuna

As for me the problem was when JSON_NUMERIC_CHECK as second argument of json_encode () passed, which casting all numbers type to int (not only integer)

至于我,问题是当 JSON_NUMERIC_CHECK 作为 json_encode () 的第二个参数传递时,它将所有数字类型转换为 int (不仅是整数)

回答by Everett Staley

You could change the [max] => 472.185 from a float into a string ([max] => '472.185') before the json_encode(). As json is a string anyway, converting your float values to strings before json_encode() will maintain the value you desire.

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