PHP7.1 json_encode() Float Issue

Это не вопрос, поскольку это больше известно. Я обновил приложение, которое использует json_encode() для PHP7.1.1, и я видел проблему с изменением поплавков, чтобы иногда расширять 17 цифр. Согласно документации, PHP 7.1.x начал использовать serialize_precision вместо точности при кодировании двойных значений. Я предполагаю, что это вызвало примерное значение

472,185

становиться

+472,18500000000006

после этого значение прошло через json_encode(). С момента своего открытия я вернулся к PHP 7.0.16, и у меня больше нет проблемы с json_encode(). Я также пытался обновить до PHP 7.1.2, прежде чем возвращаться к PHP 7.0.16.

Обоснование этого вопроса связано с PHP - Floating Number Precision, однако конец этой причине объясняется изменением точности использования serialize_precision в json_encode().

Если кто-нибудь знает о решении этой проблемы, я был бы более чем счастлив услышать аргументы/исправить.

Выдержка из многомерного массива (до):

[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"
                }
            },

Ответы

Ответ 1

Это немного подтолкнуло меня, пока я наконец не нашел эту ошибку, которая указывает вам на этот RFC, который гласит:

В настоящее время json_encode() использует EG (точность), который установлен на 14. Это означает, что максимум 14 цифр используются для отображения (печати) номера. IEEE 754 double поддерживает более высокую точность, а serialize()/var_export() использует PG (serialize_precision), который по умолчанию должен быть более точным. Поскольку json_encode() использует EG (точность), json_encode() удаляет нижние цифры частей дроби и уничтожает исходное значение, даже если PHP float может содержать более точное значение float.

И (внимание мое)

Этот RFC предлагает ввести новую настройку EG (точность) = -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}

Ответ 2

Как разработчик плагинов, я не имею общего доступа к настройкам 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 );
}

Ответ 3

У меня была та же проблема, но только serialize_precision = -1 не решила проблему. Я должен был сделать еще один шаг, чтобы обновить значение точности с 14 до 17 (как это было установлено в моем файле ini файла PHP7.0). По-видимому, изменение значения этого числа изменяет значение вычисленного поплавка.

Ответ 4

Другие решения не помогли мне. Вот что мне пришлось добавить в начале выполнения моего кода:

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

Ответ 5

Я кодировал денежные ценности и были такие вещи, как 330.46 кодирования для 330.4600000000000363797880709171295166015625. Если вы не хотите или не можете изменить настройки PHP, и вы заранее знаете структуру данных, это очень простое решение, которое сработало для меня. Просто добавьте его в строку (оба делают то же самое):

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

Для моего случая использования это было быстрое и эффективное решение. Просто имейте в виду, что это означает, что когда вы его декодируете из JSON, это будет строка, поскольку она будет завернута в двойные кавычки.

Ответ 6

Я решил эту проблему, установив для точности и serialize_precision одинаковое значение (10):

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

Вы также можете установить это в своем php.ini

Ответ 7

Вы можете изменить [max] => 472.185 из числа с плавающей точкой в строку ([max] => '472.185') перед json_encode(). Так как в любом случае json является строкой, преобразование значений с плавающей запятой в строки до того, как json_encode() сохранит желаемое значение.

Ответ 8

Что касается меня, проблема была в том, что JSON_NUMERIC_CHECK в качестве второго аргумента json_encode() прошел, что приводило все типы чисел к типу int (а не только к целому числу)