Je veux comparer deux flottants en PHP, comme dans cet exemple de code:
$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
Dans ce code, il renvoie le résultat de la else
condition au lieu de la if
condition, même si $a
et $b
sont identiques. Existe-t-il un moyen spécial de gérer / comparer les flottants en PHP?
Si oui, aidez-moi à résoudre ce problème.
Ou y a-t-il un problème avec la configuration de mon serveur?
php
floating-point
Santosh Sonarikar
la source
la source
a and b are same
. Est-ce votre code complet?floating-point
description du tag? stackoverflow.com/tags/floating-point/info C'est un comportement que vous rencontrerez probablement dans n'importe quel langage de programmation, lorsque vous utilisez des nombres à virgule flottante. Voir par exemple stackoverflow.com/questions/588004/is-javascripts-math-brokenRéponses:
Si vous le faites comme ça, ils devraient être les mêmes. Mais notez qu'une caractéristique des valeurs à virgule flottante est que les calculs qui semblent aboutir à la même valeur n'ont pas besoin d'être réellement identiques. Donc, si
$a
est un littéral.17
et$b
y arrive par un calcul, il se peut bien qu'ils soient différents, bien que les deux affichent la même valeur.Habituellement, vous ne comparez jamais les valeurs à virgule flottante pour l'égalité comme celle-ci, vous devez utiliser une plus petite différence acceptable:
Quelque chose comme ca.
la source
abs($a-$b)
>abs(($a-$b)/$b)
0.10000000000000000555111512312578270211815834045410156
est généralement inutile et ils préfèrent à la0.1
place. Et écrire un nombre pour qu'il puisse être relu exactement de la même manière. Comme vous le voyez, ce n'est pas aussi clair que vous le prétendez. Et pour mémoire, vous voulez toujours comparer des nombres à virgule flottante comme je l'ai montré, car vous pouvez arriver à$a
et$b
via différents calculs qui peuvent les rendre différents.a=b=0
et sia
est la plus petite valeur positive sans zéro possible etb
est la plus petite valeur négative non nulle possible, le test échouera de manière incorrecte. Quelques bonnes informations ici: floating-point-gui.de/errors/comparison$b
? le manuel PHP vient de faireif(abs($a-$b) < $epsilon)
le manuel MySQL a également fait de mêmeHAVING ABS(a - b) <= 0.0001
$a == $b == 0
, mais c'est déjà beaucoup plus général que l'erreur absolue. Si$a
et$b
sont dans les millions, alors vousEPSILON
devriez être très différent de si$a
et$b
êtes quelque part près de0
. Voir le lien de Dom ci-dessus pour une meilleure discussion sur ceci.Lisez d'abord l'avertissement rouge dans le manuel . Vous ne devez jamais comparer les flottants pour l'égalité. Vous devez utiliser la technique epsilon.
Par exemple:
où
PHP_FLOAT_EPSILON
est constante représentant un très petit nombre (il faut la définir dans les anciennes versions de PHP avant la 7.2)la source
EPSILON
voici une constante définie par l'utilisateur arbitraire. PHP n'a pas de constante intégrée représentant l'idée spécifique d'une architecture d'epsilon. (Voir aussiget_defined_constants
.)PHP_FLOAT_EPSILON
Plus petit nombre positif représentable x, de sorte que x + 1,0! = 1,0. Disponible à partir de PHP 7.2.0.echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */
La question est de savoir comment définir «égal» pour votre application, à quel point les nombres doivent être proches pour être considérés comme égaux.Ou essayez d'utiliser les fonctions mathématiques bc:
Résultat:
la source
bccomp()
prend des chaînes comme arguments. Quoi qu'il en soit, vous pouvez utiliserPHP_FLOAT_DIG
pour l'argument échelle.Comme indiqué précédemment, soyez très prudent lorsque vous effectuez des comparaisons en virgule flottante (qu'elles soient égales à, supérieures ou inférieures à) en PHP. Cependant, si vous n'êtes intéressé que par quelques chiffres significatifs, vous pouvez faire quelque chose comme:
L'utilisation de l'arrondi à 2 décimales (ou 3 ou 4) entraînera le résultat attendu.
la source
loose_float_compare
celle-ci pour que ce qui se passe soit évident.bccomp($a, $b, 2)
est supérieur à votre solution. Dans cet exemple, le 2 est la précision. vous pouvez le définir sur le nombre de virgules flottantes que vous souhaitez comparer.Il serait préférable d'utiliser la comparaison PHP native :
la source
Si vous avez des valeurs à virgule flottante à comparer à l'égalité, un moyen simple d'éviter le risque de stratégie d' arrondi interne du système d'exploitation, de la langue, du processeur, etc., consiste à comparer la représentation sous forme de chaîne des valeurs.
Vous pouvez utiliser l'un des éléments suivants pour produire le résultat souhaité: https://3v4l.org/rUrEq
Casting de type chaîne
Concaténation de chaînes
fonction strval
Les représentations de chaînes sont beaucoup moins difficiles que les flottants lorsqu'il s'agit de vérifier l'égalité.
la source
(string)
opération de conversion est effectuée par référence, en modifiant la déclaration d'origine? Si tel n'est pas le cas 3v4l.org/CraasSi vous avez un petit nombre fini de points décimaux qui sera acceptable, ce qui suit fonctionne bien (bien qu'avec des performances plus lentes que la solution epsilon):
la source
Cela fonctionne pour moi sur PHP 5.3.27.
la source
Pour PHP 7.2, vous pouvez travailler avec PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):
la source
==
et!=
non>
,>=
,<
,<=
Si vous l'écrivez comme ça, cela fonctionnera probablement, alors j'imagine que vous l'avez simplifié pour la question. (Et garder la question simple et concise est normalement une très bonne chose.)
Mais dans ce cas, j'imagine qu'un résultat est un calcul et un résultat est une constante.
Cela viole une règle cardinale de la programmation en virgule flottante: ne jamais faire de comparaisons d'égalité.
Les raisons en sont un peu subtiles 1, mais ce qui est important de se rappeler, c'est qu'elles ne fonctionnent généralement pas (sauf, ironiquement, pour les valeurs intégrales) et que l'alternative est une comparaison floue du type:
1. L'un des problèmes majeurs concerne la manière dont nous écrivons les nombres dans les programmes. Nous les écrivons sous forme de chaînes décimales et, par conséquent, la plupart des fractions que nous écrivons n'ont pas de représentation exacte de la machine. Ils n'ont pas de formes finies exactes car ils se répètent en binaire. Chaque fraction de machine est un nombre rationnel de la forme x / 2 n . Maintenant, les constantes sont décimales et chaque constante décimale est un nombre rationnel de la forme x / (2 n * 5 m ). Les nombres de 5 m sont impairs, il n'y a donc pas de facteur 2 n pour aucun d'entre eux. Ce n'est que lorsque m == 0 qu'il y a une représentation finie dans le développement binaire et décimal de la fraction. Donc, 1,25 est exact car c'est 5 / (2 2 * 5 0) mais 0,1 n'est pas parce que c'est 1 / (2 0 * 5 1 ). En fait, dans la série 1.01 .. 1.99, seuls 3 des nombres sont exactement représentables: 1,25, 1,50 et 1,75.
la source
round($float, 3) == round($other, 3)
Voici la solution pour comparer des virgules flottantes ou des nombres décimaux
Cast une
decimal
variable surstring
et tout ira bien.la source
La comparaison des flottants pour l'égalité a un algorithme naïf O (n).
Vous devez convertir chaque valeur flottante en chaîne, puis comparer chaque chiffre en commençant par le côté gauche de la représentation sous forme de chaîne de chaque flottant à l'aide d'opérateurs de comparaison d'entiers. PHP diffusera automatiquement le chiffre de chaque position d'index en un entier avant la comparaison. Le premier chiffre plus grand que l'autre cassera la boucle et déclarera le flottant auquel il appartient comme le plus grand des deux. En moyenne, il y aura 1/2 * n comparaisons. Pour les flottants égaux les uns aux autres, il y aura n comparaisons. Il s'agit du pire des cas pour l'algorithme. Le meilleur des cas est que le premier chiffre de chaque flottant est différent, ne provoquant qu'une seule comparaison.
Vous ne pouvez pas utiliser INTEGER COMPARISON OPERATORS sur des valeurs flottantes brutes dans le but de générer des résultats utiles. Les résultats de ces opérations n'ont aucune signification car vous ne comparez pas des entiers. Vous violez le domaine de chaque opérateur qui génère des résultats dénués de sens. Cela vaut également pour la comparaison delta.
Utilisez des opérateurs de comparaison d'entiers pour ce pour quoi ils sont conçus: comparer des entiers.
SOLUTION SIMPLIFIÉE:
la source
2019
TL; DR
Utilisez ma fonction ci-dessous, comme ceci
if(cmpFloats($a, '==', $b)) { ... }
cmpFloats($a, '<=', $b)
vsbccomp($a, $b) <= -1
Résumé
Je vais dévoiler le mystère.
Donc, si vous essayez ce qui suit, ce sera égal:
Comment obtenir la valeur réelle du float?
Comment pouvez-vous comparer?
==
et!=
, vous pouvez les convertir en chaînes, cela devrait fonctionner parfaitement:Tapez cast avec corde :
Ou transtypé avec
number_format()
:Avertissement:
Évitez les solutions qui impliquent de manipuler mathématiquement les flottants (multiplier, diviser, etc.) puis de comparer, la plupart du temps, elles résoudront certains problèmes et introduiront d'autres problèmes.
Solution suggérée
J'ai créé une fonction PHP pure (aucun depenedcies / bibliothèques / extensions nécessaires). Vérifie et compare chaque chiffre sous forme de chaîne. Fonctionne également avec des nombres négatifs.
la source
La fonction de @evilReiko a quelques bugs comme ceux-ci:
Dans ma fonction, j'ai corrigé ces bugs, mais de toute façon dans certains cas, cette fonction renvoie de mauvaises réponses:
Fonction fixe pour comparer les flottants
Répondez à votre question
la source
Voici une classe utile de ma bibliothèque personnelle pour traiter les nombres à virgule flottante. Vous pouvez l'ajuster à votre guise et insérer la solution de votre choix dans les méthodes de classe :-).
la source
Réponse simple:
la source