簡體   English   中英

PHP7.1 json_encode() 浮動問題

[英]PHP7.1 json_encode() Float Issue

這不是一個問題,因為它更像是一個注意事項。 我將一個使用json_encode()的應用程序更新到 PHP7.1.1,我發現浮點數被更改為有時擴展到 17 位數字的問題。 根據文檔,PHP 7.1.x 在編碼雙精度值時開始使用serialize_precision而不是 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()從 precision 到 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"
                }
            },

這讓我有點發瘋,直到我終於找到了這個錯誤,它指向這個 RFC ,它說

目前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 值。

和(強調我的)

這個 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

您可以使用此命令行驗證它是否有效

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

你應該得到

{"price":45.99}

作為插件開發人員,我無法訪問服務器的 php.ini 設置。 因此,根據 Machavity 的回答,我編寫了一小段代碼,您可以在 PHP 腳本中使用它。 只需將它放在腳本之上,json_encode 就會照常工作。

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

在某些情況下,需要再設置一個變量。 我將此添加為第二個解決方案,因為我不確定第二個解決方案是否在第一個解決方案已被證明有效的所有情況下都能正常工作。

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

我通過將 precision 和 serialize_precision 設置為相同的值(10)解決了這個問題:

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

您也可以在 php.ini 中進行設置

我正在編碼貨幣價值,並將330.46編碼為330.4600000000000363797880709171295166015625類的東西。 如果您不想或不能更改 PHP 設置並且您事先知道數據的結構,那么有一個非常簡單的解決方案對我有用。 只需將其強制轉換為字符串(以下兩者都做同樣的事情):

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

對於我的用例,這是一個快速有效的解決方案。 請注意,這意味着當您將其從 JSON 解碼回來時,它將是一個字符串,因為它將用雙引號括起來。

我有同樣的問題,但只有 serialize_precision = -1 沒有解決問題。 我不得不再做一步,將精度值從 14 更新到 17(因為它是在我的 PHP7.0 ini 文件中設置的)。 顯然,更改該數字的值會更改計算浮點數的值。

其他解決方案對我不起作用。 這是我在代碼執行開始時必須添加的內容:

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

在 php 7.2.32 上,解決方案是在 php.ini 中設置:

precision=10
serialize_precision=10

至於我,問題是當JSON_NUMERIC_CHECK作為 json_encode () 的第二個參數被傳遞時,它將所有(不僅是整數)數字類型轉換為 int。

使用number_format將其存儲為具有您需要的精確精度的字符串,然后使用JSON_NUMERIC_CHECK選項對其進行json_encode

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

你得到:

{"max": 472.185}

請注意,這會將源對象中的所有數字字符串編碼為結果 JSON 中的數字。

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

serializeserialize_precision設置為不同的值時,似乎會出現問題。 在我的情況下分別是 14 和 17。 將它們都設置為 14 解決了該問題,將serialize_precision設置為 -1 也是如此。

PHP 7.1.0 serialize_precisionserialize_precision的默認值已更改為 -1,這意味着“將使用一種用於舍入此類數字的增強算法”。 但是,如果您仍然遇到此問題,則可能是因為您有先前版本的 PHP 配置文件。 (也許您在升級時保留了配置文件?)

要考慮的另一件事是在您的情況下使用浮點值是否有意義。 使用包含數字的字符串值來確保 JSON 中始終保留正確的小數位數可能有意義也可能沒有意義。

您可以在 json_encode() 之前將 [max] => 472.185 從浮點數更改為字符串 ([max] => '472.185')。 由於 json 無論如何都是字符串,因此在 json_encode() 之前將您的浮點值轉換為字符串將保持您想要的值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM