web-dev-qa-db-ja.com

PHP7.1 json_encode()フロートの問題

それはより意識しているので、これは質問ではありません。 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-浮動小数点精度 に由来しますが、このすべての理由は、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"
                }
            },
60
Gwi7d31

このバグ があなたを指し示す このRFC

現在、json_encode()は、14に設定されたEG(精度)を使用します。つまり、最大14桁が数字の表示(印刷)に使用されます。 IEEE 754 doubleはより高い精度をサポートし、serialize()/var_export()はPG(serialize_precision)を使用します。これは17に設定するとより正確になります。 json_encode()はEG(精度)を使用するため、PHPのfloatがより正確なfloat値を保持できる場合でも、json_encode()は小数部の下位桁を削除し、元の値を破棄します。

そして(エンファシス鉱山)

このRFCは、浮動小数点数を丸めるためのより良いアルゴリズムを使用するzend_dtoa()のモード0を使用する新しい設定EG(precision)=-1およびPG(serialize_precision)=-1を導入することを提案しています-1は、0モードを示すために使用されます)

要するに、PHP 7.1 json_encodeに新しく改善された精度エンジンを使用させる新しい方法があります。 php.iniserialize_precisionを変更する必要があります

serialize_precision = -1

このコマンドラインで動作することを確認できます

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

取得する必要があります

{"price":45.99}
67
Machavity

プラグイン開発者として、サーバーのphp.ini設定への一般的なアクセス権はありません。そこで、Machavityの答えに基づいて、PHPスクリプトで使用できるこの小さなコードを書きました。スクリプトの上に置くだけで、json_encodeは通常どおり動作し続けます。

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

場合によっては、もう1つの変数を設定する必要があります。これを2番目のソリューションとして追加します。最初のソリューションが機能することが証明されたすべてのケースで、2番目のソリューションが正常に機能するかどうかわからないからです。

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

私は同じ問題を抱えていましたが、serialize_precision = -1のみが問題を解決しませんでした。精度の値を14から17に更新するために、もう1つの手順を実行する必要がありました(PHP7.0 iniファイルで設定されていたため)。どうやら、その数の値を変更すると、計算されたフロートの値が変更されます。

3
Alin Pop

他の解決策はうまくいきませんでした。コード実行の最初に追加しなければならなかったものは次のとおりです。

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}
2
Mike P. Sinn

私は金銭的価値をエンコードしていましたが、330.46330.4600000000000363797880709171295166015625にエンコードしていました。 PHP設定を変更したくない、またはできない場合、データの構造が事前にわかっている場合は、非常に簡単な解決策があります。単純に文字列にキャストします(次の両方で同じことが行われます)。

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

私のユースケースでは、これは迅速かつ効果的なソリューションでした。これは、JSONからデコードして戻すと、二重引用符で囲まれるため文字列になることを意味することに注意してください。

2
texelate

Precisionとserialize_precisionの両方を同じ値に設定することでこれを解決しました(10):

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

Php.iniでこれを設定することもできます

0
whatever_sa

私に関しては、json_encode()の2番目の引数としてJSON_NUMERIC_CHECKが渡されたときに問題が発生しました。

0
Acuna

Json_encode()の前に、[max] => 472.185をフロートから文字列([max] => '472.185')に変更できます。とにかくjsonは文字列なので、json_encode()の前にfloat値を文字列に変換すると、希望する値が維持されます。

0
Everett Staley