123 votes

PHP7.1 json_encode () Float Issue

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

472.185

pour devenir

472.18500000000006

après que la valeur est passé par json_encode(). Depuis ma découverte, j'ai repris l'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 souches à partir de PHP - Nombre Flottant de Précision, cependant à la fin la raison pour cela est dû à la variation de la précision de serialize_precision d'utilisation en json_encode().

Si quelqu'un connais une solution à ce problème, je serais plus qu'heureux de les écouter sur l'raisonnement/fix.

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

119voto

Machavity Points 7587

Ce qui m'a poussé les noix pour un peu jusqu'à ce que finalement, j'ai trouvé ce bug qui vous renvoie à cette 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 l'affichage (impression) le nombre. La norme IEEE 754 double soutient l'augmentation de la précision et de l' serialize()/var_export() utilise PG(serialize_precision) qui a fixé à 17 la valeur par défaut pour être plus précis. Depuis json_encode() utilise par exemple(de précision), json_encode() supprime les chiffres inférieurs de la fraction des pièces et détruit de la valeur d'origine, même si PHP flottant pourrait détenir plus précise de la valeur flottante.

Et (l'emphase est mienne)

Cette RFC propose d'introduire un nouveau paramètre EG(de précision)=-1 et PG(serialize_precision)=-1 qui utilise zend_dtoa()'s mode 0 qui utilise mieux algorigthm pour arrondir les nombres réels (-1 est utilisé pour indiquer 0 mode).

En bref, il y a une nouvelle façon de faire de PHP 7.1 json_encode utiliser le nouveau et amélioré la précision du moteur. En php.ini vous avez besoin de changer de serialize_precision de

serialize_precision = -1

Vous pouvez vérifier que cela fonctionne avec cette ligne de commande

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

Vous devriez obtenir

{"price":45.99}

52voto

alev Points 516

En tant que développeur de plugins, je n'ai pas d'accès général aux paramètres php.ini 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. Il suffit de le placer en haut du script et json_encode continuera à fonctionner normalement.

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

6voto

texelate Points 1157

J'ai été encodage des valeurs monétaires et avait des choses comme 330.46 de l'encodage 330.4600000000000363797880709171295166015625. Si vous ne souhaitez pas ou ne pouvez pas modifier les paramètres de PHP et vous connaissez la structure de données à l'avance, il y a une solution très simple qui a fonctionné pour moi. Simplement la convertir en une chaîne de caractères (à la fois faire la même chose):

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

Pour mon utilisation, il est un moyen rapide et efficace de la solution. Il suffit de noter que cela signifie quand vous le décoder retour de JSON, il sera une chaîne de caractères depuis ça va être enveloppé dans des guillemets doubles.

3voto

Alin Pop Points 81

J'ai eu le même problème mais seul serialize_precision = -1 n'a pas résolu le problème. Je devais faire un pas de plus, pour mettre à jour la valeur de précision de 14 à 17 (comme elle était définie sur mon fichier in7 PHP7.0). Apparemment, changer la valeur de ce nombre change la valeur du float calculé.

3voto

Mike P. Sinn Points 84

Les autres solutions n'ont pas fonctionné pour moi. Voici ce que je devais 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 );
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X