web-dev-qa-db-fra.com

Comparer les flotteurs en php

Je veux comparer deux floats 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, le résultat de la condition else est renvoyé au lieu de la condition if, même si $a et $b sont identiques. Existe-t-il un moyen spécial de manipuler/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?

117
Santosh Sonarikar

Si vous le faites comme ça, ils devraient être les mêmes. Mais notons qu'une des caractéristiques des valeurs en virgule flottante est que les calculs qui semblent donner la même valeur n'ont pas besoin d'être identiques. Donc, si $a est un littéral .17 et $b y parvient par un calcul, il se peut bien qu’ils soient différents, bien que les deux affichent la même valeur.

En règle générale, vous ne comparez jamais les valeurs en virgule flottante pour l'égalité, vous devez utiliser la plus petite différence acceptable:

if (abs(($a-$b)/$b) < 0.00001) {
  echo "same";
}

Quelque chose comme ca.

214
Joey

Lisez d'abord l'avertissement rouge http://www.php.net/manual/fr/language.types.float.php . Vous ne devez jamais comparer les flottants pour l'égalité. Vous devriez utiliser la technique epsilon.

Par exemple:

if (abs($a-$b) < EPSILON) { … }

EPSILON est une constante représentant un très petit nombre (vous devez le définir)

48
Andrey

Ou essayez d’utiliser des fonctions mathématiques bc:

<?php
$a = 0.17;
$b = 1 - 0.83; //0.17

echo "$a == $b (core comp oper): ", var_dump($a==$b);
echo "$a == $b (with bc func)  : ", var_dump( bccomp($a, $b)==0 );

Résultat:

0.17 == 0.17 (core comp oper): bool(false)
0.17 == 0.17 (with bc func)  : bool(true)
21
Mario

Comme indiqué précédemment, soyez très prudent lorsque vous effectuez des comparaisons en virgule flottante (é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:

$a = round(0.17, 2);
$b = round(1 - 0.83, 2); //0.17
if($a == $b ){
    echo 'a and b are same';
}
else {
    echo 'a and b are not same';
}

Le résultat attendu est arrondi à 2 décimales (ou 3 ou 4). 

13
Michael Butler

Il serait préférable d’utiliser la comparaison native PHP :

bccomp($a, $b, 3)
// Third parameter - the optional scale parameter
// is used to set the number of digits after the decimal place
// which will be used in the comparison. 

Retourne 0 si les deux opérandes sont égaux, 1 si le left_operand est plus grand que le right_operand, -1 sinon.

9
FieryCat

Si vous avez des valeurs à virgule flottante à comparer à l’égalité, un moyen simple d’éviter le risque dearrondissement internede la stratégie du système d’exploitation, de la langue, du processeur ou autre, consiste à comparer la chaînereprésentationdes valeurs, comme:

if ( (string) $a === (string) $b) { … }

Les représentations de chaîne sont beaucoup moins pointilleuses que les flottants lorsqu'il s'agit de vérifier l'égalité.

5
Ame Nomade

Voici la solution pour comparer des points flottants ou des nombres décimaux

//$fd['someVal'] = 2.9;
//$i for loop variable steps 0.1
if((string)$fd['someVal']== (string)$i)
{
    //Equal
}

Transformez une variable decimal en string et tout ira bien.

2
Natalie Rey

Cela fonctionne pour moi sur PHP 5.3.27.

$payments_total = 123.45;
$order_total = 123.45;

if (round($payments_total, 2) != round($order_total, 2)) {
   // they don't match
}
2
crmpicco

Si vous avez un petit nombre fini de points décimaux acceptable, les mesures suivantes fonctionnent parfaitement (bien que les performances soient plus lentes que la solution epsilon):

$a = 0.17;
$b = 1 - 0.83; //0.17

if (number_format($a, 3) == number_format($b, 3)) {
    echo 'a and b are same';
} else {
    echo 'a and b are not same';
}
2
dtbarne

Si vous écrivez juste 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 fondamentale de la programmation en virgule flottante:Ne faites jamais de comparaisons d’égalité.

Les raisons en sont un peu subtiles1 mais il est important de se rappeler qu’elles ne fonctionnent généralement pas (sauf, ironiquement, pour les valeurs intégrales) et que l’alternative consiste en une comparaison floue du type:

if abs(a - y) < epsilon



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/2n. Maintenant, les constantes sont décimales et chaque constante décimale est un nombre rationnel de la forme x/(2n * 5m). Le 5m les nombres sont impairs, donc il n'y a pas de 2n facteur pour l'un d'entre eux. Ce n'est que lorsque m == 0 qu'il y a une représentation finie à la fois dans l'expansion binaire et décimale de la fraction. Donc, 1,25 est exact car c’est 5/(22* 5) mais 0,1 n’est pas parce que c’est 1/(2* 51). 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.

1
DigitalRoss

La comparaison des flottants pour l'égalité a un algorithme naïf O(n).

Vous devez convertir chaque valeur float en chaîne, puis comparer chaque chiffre en partant du côté gauche de la représentation de chaque chaîne en utilisant des opérateurs de comparaison d'entiers. PHP mettra automatiquement le chiffre de chaque position d'index en un entier avant la comparaison. Le premier chiffre plus grand que l'autre rompra 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 des flotteurs égaux, il y aura n comparaisons. C'est le pire des cas pour l'algorithme. Dans le meilleur des cas, le premier chiffre de chaque flottant est différent, ce qui entraîne une seule comparaison.

Vous ne pouvez pas utiliser OPÉRATEURS DE COMPARAISON INTEGER sur des valeurs float brutes dans le but de générer des résultats utiles. Les résultats de telles opérations n'ont aucune signification, car vous ne comparez pas les entiers. Vous violez le domaine de chaque opérateur, ce qui génère des résultats sans signification. Ceci est également valable pour la comparaison delta.

Utilisez les opérateurs de comparaison d’entiers pour ce pour quoi ils sont conçus: comparer des entiers.

SOLUTION SIMPLIFIÉE:

<?php

function getRand(){
  return ( ((float)mt_Rand()) / ((float) mt_getrandmax()) );
 }

 $a = 10.0 * getRand();
 $b = 10.0 * getRand();

 settype($a,'string');
 settype($b,'string');

 for($idx = 0;$idx<strlen($a);$idx++){
  if($a[$idx] > $b[$idx]){
   echo "{$a} is greater than {$b}.<br>";
   break;
  }
  else{
   echo "{$b} is greater than {$a}.<br>";
   break;
  }
 }

?>
1
Kyle

Pour PHP 7.2, vous pouvez utiliser PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):

if(abs($a-$b) < PHP_FLOAT_EPSILON){
   echo 'a and b are same';
}
1
Gladhon

Un piège négligé ici ...

Si vous utilisez des flottants signés, vous souhaiterez effectuer deux comparaisons pour vérifier la proximité: 

$a - $b < EPSILON && $b - $a < EPSILON
0
nathan kozyra

2019

Je vais dévoiler le mystère.

$a = 0.17;
$b = 1 - 0.83;// 0.17 (output)
              // but actual value internally is: 0.17000000000000003996802888650563545525074005126953125
if($a == $b) {
    echo 'a and b are same';
} else {
    echo 'a and b are not same';
}

Donc si vous essayez le ci-dessous, ce sera égal:

if($b == 0.17000000000000003) {
    echo 'if';
} else {
    echo 'else';
}
// it will output "if"

Comment obtenir la valeur réelle de float?

$b = 1 - 0.83;
echo $b;// 0.17
echo number_format($a, 100);// 0.1700000000000000399680288865056354552507400512695312500000000000000000000000000000000000000000000000

Comment pouvez-vous comparer?

  1. Utilisez Fonctions BC Math . (vous aurez toujours beaucoup de moments wtf-aha-gotcha)
  2. Si vous comparez des floats avec == et !=, vous pouvez les transtyper en chaînes, cela devrait fonctionner parfaitement:

Type cast avec string:

$b = 1 - 0.83;
if((string)$b === (string)0.17) {
    echo 'if';
} else {
    echo 'else';
}
// it will output "if"

Ou transtypé avec number_format():

$b = 1 - 0.83;
if(number_format($b, 3) === number_format(0.17, 3)) {
    echo 'if';
} else {
    echo 'else';
}
// it will output "if"

Attention:

Évitez les solutions qui impliquent une manipulation mathématique des flotteurs (multiplication, division, etc.) puis une comparaison. Elles permettent généralement de résoudre certains problèmes et d’en introduire d’autres.

0
evilReiko
//You can compare if less or more.

$parcela='250.23'; //total value
$tax = (double) '15.23'; //tax value
$taxaPercent=round((100*$tax)/$parcela,2); //tax percent 

$min=(double) '2.50';// minimum tax percent                             

if($taxaPercent < $min ){
    // tax error tax is less than 2.5
}
0
GustavoVargas

Je déteste le dire, mais "travaille pour moi":

Beech:~ adamw$ php -v
PHP 5.3.1 (cli) (built: Feb 11 2010 02:32:22) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies
Beech:~ adamw$ php -f test.php
a and b are same

Maintenant, les comparaisons en virgule flottante sont en général délicates - les choses que vous pourriez penser identiques ne le sont pas (à cause des erreurs d’arrondi et/ou des nuances de représentation). Vous voudrez peut-être lire http://floating-point-gui.de/

0
Adam Wright
if( 0.1 + 0.2 == 0.3 ){
 echo 'a and b are same';
}
else {
 echo 'a and b are not same';
}

Cela causera problèmes , à cause de l'arithmétique à virgule flottante standard IEEE (qui a ce problème).

0
gblazex

J'ai fini simplement par:

  uasort($_units[$k], function($a, $b)
                     {
                        $r = 0;
                        if ($a->getFloatVal() > $b->getFloatVal()) $r = 1;
                        if ($a->getFloatVal() < $b->getFloatVal()) $r = -1;
                        //print_r(["comparing {$a->getFloatVal()} vs {$b->getFloatVal()} res {$r}"]);
                        return $r * -1;
                      }
  );    
0
luky