PHP7.1 JSON encode() Float Issue

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

472.185

стать

472.18500000000006

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

обоснование этого вопроса исходит из PHP-точность плавающего числа, однако конец все причины для этого из-за изменения от точности к использованию 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"
                }
            },

5 ответов


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

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

и (выделено мной)

этот RFC предлагает ввести новую настройку, например (точность)=-1 и ПГ(serialize_precision)=-1, что использует zend_dtoa()'ы режим 0, который использует лучше algorigthm для округления числа с плавающей точкой (-1 используется, чтобы указать 0 режим).

короче говоря, есть новый способ сделать PHP 7.1 json_encode используйте новый и улучшенный двигатель точности. В php.ini вам нужно изменить serialize_precision to

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 );
}

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


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

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

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

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

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