PHP7.1 JSON encodage () Float Issue

ce n'est pas une question car il est plus d'un être conscient. J'ai mis à jour une application qui utilise json_encode() à PHP7.1.1 et je voyais un problème avec les flotteurs changés pour parfois étendre 17 chiffres. Selon la documentation, PHP 7.1.x a commencé à utiliser serialize_precision au lieu de la précision lors de l'encodage de valeurs doubles. Je suppose que cela a causé une valeur d'exemple de

472.185

pour devenir

472.18500000000006

après cette valeur est passé par json_encode() . Depuis ma découverte, je suis revenu à PHP 7.0.16 et je n'ai plus le problème avec json_encode() . J'ai aussi essayé de mettre à jour vers PHP 7.1.2 avant de revenir à PHP 7.0.16.

le raisonnement derrière cette question ne provient de PHP-Floating Number Precision , mais la fin toute la raison pour cela est en raison du passage de la précision à l'utilisation de serialize_precision dans json_encode() .

si quelqu'un connaît une solution à ce problème, je serais plus qu'heureux d'écouter le raisonnement/correction.

Extrait de tableau multidimensionnel (avant):

[staticYaxisInfo] => Array
                    (
                        [17] => stdClass Object
                            (
                                [variable_id] => 17
                                [static] => 1
                                [min] => 0
                                [max] => 472.185
                                [locked_static] => 1
                            )

                    )

et après avoir traversé json_encode() ...

"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },
35
demandé sur Machavity 2017-03-23 19:11:14

5 réponses

cela m'a rendu un peu dingue jusqu'à ce que j'ai finalement trouvé ce bug qui vous indique ce RFC qui dit

Actuellement json_encode() utilise par exemple(de précision) qui est fixé à 14. Cela signifie que 14 chiffres au plus sont utilisés pour afficher (imprimer) le numéro. IEEE 754 double supporte une plus grande précision et serialize() / var_export() utilise PG (serialize_precision) qui définit à 17 be par défaut être plus précis. Depuis json_encode() utilise EG (précision), json_encode() enlève les chiffres inférieurs des parties de fraction et détruit la valeur originale même si le flotteur de PHP pourrait tenir la valeur flottante plus précise.

Et (l'emphase est mienne)

ce RFC propose d'introduire un nouveau réglage EG (precision) = -1 et PG (serialize_precision) = -1 qui utilise le mode 0 de zend_dtoa () qui utilise un meilleur algorithme pour arrondir les nombres flottants (-1 est utilisé pour indiquer 0 mode) .

en bref, il y a une nouvelle façon de faire PHP 7.1 json_encode utiliser le nouveau moteur de précision améliorée. Dans php.ini vous devez remplacer serialize_precision par

"
serialize_precision = -1

vous pouvez vérifier qu'il fonctionne avec cette ligne de commande

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

Vous devriez obtenir

{"price":45.99}
45
répondu Machavity 2017-03-27 20:51:57

en tant que développeur de plugin, je n'ai pas d'accès général au php.paramètres d'initialisation d'un serveur. Donc, basé sur la réponse de Machavity j'ai écrit ce petit morceau de code que vous pouvez utiliser dans votre script PHP. Mettez-le simplement en haut du script et json_encode continuera de fonctionner comme d'habitude.

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}
7
répondu alev 2017-09-24 13:06:37

j'ai eu le même problème mais seulement serialize_precision = -1 n'a pas résolu le problème. J'ai dû faire un pas de plus, pour mettre à jour la valeur de précision de 14 à 17 (comme il a été mis sur mon PHP7.0 fichier ini). Apparemment, changer la valeur de ce nombre change la valeur du flotteur calculé.

3
répondu Alin Pop 2017-12-22 08:49:23

les autres solutions n'ont pas fonctionné pour moi. Voici ce que j'ai dû ajouter au début de l'exécution de mon code:

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}
3
répondu mikepsinn 2018-01-23 21:51:07

j'encodais des valeurs monétaires et j'avais des choses comme 330.46 encodant en 330.4600000000000363797880709171295166015625 . Si vous ne souhaitez pas, ou ne pouvez pas, modifier les paramètres de PHP et que vous connaissez la structure des données à l'avance, il y a une solution très simple qui a fonctionné pour moi. Il suffit de le lancer sur une corde (les deux suivants font la même chose):

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

pour mon cas d'utilisation ce fut une solution rapide et efficace. Notez juste que cela signifie que lorsque vous le décodez en arrière de JSON il sera sois une ficelle car elle sera entourée de doubles guillemets.

0
répondu texelate 2018-06-20 09:08:23