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 (а не только к целому числу)