PHP : problème de comparaison égalité dans boucle FOR avec incrément décimal de 0.1

WRInaute passionné
Bonjour,
Dans une boucle FOR avec un in crément décimal de 0.1, il m'est impossible de comparer la variable dans la boucle avec une autre varaible

JE m'explique, J'ai le code suivant :
Code:
$poids_min=0.5;
                        $poids_max=7;
 
                            print('<select name="prono_result_poids" id="prono_result_poids" class="zone_select">');
                                print('<option value="">xxxxxxxxxxxxxxxx</option>');
                                for ($jj=$poids_min;$jj<=$poids_max;$jj=$jj+0.1) {
                                    $poids_double=floatval($row_prono["prono_result_poids"]);  // issue d'une BDD mysql, colonne au format decimal
                                    $tmp_var=(isset($row_prono["prono_result_poids"]) AND $poids_double==$jj)?" selected ":"";
                                    print('<option value="'.$jj.'" '.$tmp_var.'>'.$jj.'</option>');
                                }
                            print('</select> kg');

et l'égalité ne fonctionne jamais et donc ne sélectionne jamais la bonne ligne dans le SELECT (ça reste au début), ici $row_prono["prono_result_poids"] vaut 2.2

j'ai converti la donnée issue de ma BDD en double (floatval) étant donné que $jj est un double mais ça ne fonctionne jamais

quand je mets une égalite en dure, bien entendu ça fonctionne
 
WRInaute impliqué
Code:
$poids_min=0.5;
$poids_max=7;

$html_options = '';
for ($jj=$poids_min; $jj<=$poids_max; $jj+=0.1) {
    $is_selected = (isset($row_prono["prono_result_poids"]) && (float) $row_prono["prono_result_poids"] === $jj);
    $html_options .= '<option value="'.$jj.'"'. ($is_selected ? ' selected' : '') .'>'.$jj.'</option>';
}

print '<select name="prono_result_poids" id="prono_result_poids" class="zone_select">
    . '<option value="">xxxxxxxxxxxxxxxx</option>'
    . $html_options
    . '</select> kg';

Je ne m'attend pas à ce que ça change grand-chose, parce qu'il faudrait savoir ce que tu as vraiment dans
$row_prono["prono_result_poids"] (avec des var_dump()).
 
WRInaute passionné
Je ne m'attend pas à ce que ça change grand-chose,
non le float ne change rien, j'avais déjà fait le test

SOlution : en fait il ne faut pas utiliser d'incrément décimal dans une boucle FOR

il y a des incompatibilités (à priori connues, de nombreux articles et forums en parle) dans la précision des FLOAT à ce niveau

il vaut donc mieux travailler avec des entiers dans la boucle, puis diviser par 10 dans le corps de cette boucle :
 
WRInaute impliqué
Je t'avouerais que je n'avais jamais fait de boucle for avec une décimale : effectivement, $jj est bonne pour la camisole. Autant avec des divisions/multiplications, je connaissais le problème, mais pour de simples additions...

Code:
float(0.6)
float(0.7)
float(0.7999999999999999)
float(0.8999999999999999)
float(0.9999999999999999)
float(1.0999999999999999)
float(1.2)
float(1.3)
float(1.4000000000000001)
float(1.5000000000000002)
float(1.6000000000000003)
float(1.7000000000000004)
float(1.8000000000000005)
float(1.9000000000000006)
float(2.0000000000000004)
float(2.1000000000000005)
float(2.2000000000000006)
float(2.3000000000000007)
float(2.400000000000001)
float(2.500000000000001)
float(2.600000000000001)
float(2.700000000000001)
float(2.800000000000001)
float(2.9000000000000012)
float(3.0000000000000013)
float(3.1000000000000014)
float(3.2000000000000015)
float(3.3000000000000016)
float(3.4000000000000017)
float(3.5000000000000018)
float(3.600000000000002)
float(3.700000000000002)
float(3.800000000000002)
float(3.900000000000002)
float(4.000000000000002)
float(4.100000000000001)
float(4.200000000000001)
float(4.300000000000001)
float(4.4)
float(4.5)
float(4.6)
float(4.699999999999999)
float(4.799999999999999)
float(4.899999999999999)
float(4.999999999999998)
float(5.099999999999998)
float(5.1999999999999975)
float(5.299999999999997)
float(5.399999999999997)
float(5.4999999999999964)
float(5.599999999999996)
float(5.699999999999996)
float(5.799999999999995)
float(5.899999999999995)
float(5.999999999999995)
float(6.099999999999994)
float(6.199999999999994)
float(6.299999999999994)
float(6.399999999999993)
float(6.499999999999993)
float(6.5999999999999925)
float(6.699999999999992)
float(6.799999999999992)
float(6.8999999999999915)
float(6.999999999999991)
 
WRInaute accro
Avec range() c'est mieux non ?

PHP:
<?php
$poids = range(0.5, 7, 0.1);
$row_prono = ['prono_result_poids' => 2.2];
$options = '';
foreach ($poids as $p) {
    $selected = $row_prono['prono_result_poids'] === $p ? ' selected' : '';
    $options .= '<option value="' . $p . '"' . $selected . '>' . $p . '</option>';
}

?>
<select><?php echo $options; ?></select>
 
WRInaute impliqué
Avec range() c'est mieux non ?
Pas forcément... et pourtant ça ne donne pas la même chose qu'une boucle for.

Sur mon serveur dédié avec la dernière version de PHP7.4, j'ai exactement les valeurs attendu, hourra... mais si je re-teste ma boucle for, j'ai aussi les bonnes valeurs.

Par contre sur ma machine de dev dont j'ai affiché le var_dump de la boucle for plus haut, et qui est en PHP 8 :
var_dump(range(0.5, 7, 0.1)); donne le résultat ci-dessous.

Ça n'est pas la même chose qu'avec la boucle for (hem...), mais ça n'est pas fiable non plus (double hem...). Donc range pourrait fonctionner aussi, apparemment, dans certains cas... mais il est vivement conseillé de tester avant sur la machine de dev et le serveur de prod, il y a des risques de mauvaises surprises.

Pour continuer dans le fun, je me demandais ce que ça valait au niveau perfs, range (même si dans ce cas précis, ça serait une micro optimisation). Par rapport à la boucle for, c'est environ 2,5x plus lent sur mon dédié en 7.4, mais c'est presque 2x plus rapide sur ma machine de dev en 8.
Bon, on va laisser le tomber perf, donc, ne reste plus que la lisibilité — j'aime assez range(), je dois dire.

Code:
array(66) {
  [0]=>
  float(0.5)
  [1]=>
  float(0.6)
  [2]=>
  float(0.7)
  [3]=>
  float(0.8)
  [4]=>
  float(0.9)
  [5]=>
  float(1)
  [6]=>
  float(1.1)
  [7]=>
  float(1.2000000000000002)
  [8]=>
  float(1.3)
  [9]=>
  float(1.4)
  [10]=>
  float(1.5)
  [11]=>
  float(1.6)
  [12]=>
  float(1.7000000000000002)
  [13]=>
  float(1.8)
  [14]=>
  float(1.9000000000000001)
  [15]=>
  float(2)
  [16]=>
  float(2.1)
  [17]=>
  float(2.2)
  [18]=>
  float(2.3)
  [19]=>
  float(2.4000000000000004)
  [20]=>
  float(2.5)
  [21]=>
  float(2.6)
  [22]=>
  float(2.7)
  [23]=>
  float(2.8000000000000003)
  [24]=>
  float(2.9000000000000004)
  [25]=>
  float(3)
  [26]=>
  float(3.1)
  [27]=>
  float(3.2)
  [28]=>
  float(3.3000000000000003)
  [29]=>
  float(3.4000000000000004)
  [30]=>
  float(3.5)
  [31]=>
  float(3.6)
  [32]=>
  float(3.7)
  [33]=>
  float(3.8000000000000003)
  [34]=>
  float(3.9000000000000004)
  [35]=>
  float(4)
  [36]=>
  float(4.1)
  [37]=>
  float(4.2)
  [38]=>
  float(4.300000000000001)
  [39]=>
  float(4.4)
  [40]=>
  float(4.5)
  [41]=>
  float(4.6000000000000005)
  [42]=>
  float(4.7)
  [43]=>
  float(4.8)
  [44]=>
  float(4.9)
  [45]=>
  float(5)
  [46]=>
  float(5.1000000000000005)
  [47]=>
  float(5.2)
  [48]=>
  float(5.300000000000001)
  [49]=>
  float(5.4)
  [50]=>
  float(5.5)
  [51]=>
  float(5.6000000000000005)
  [52]=>
  float(5.7)
  [53]=>
  float(5.800000000000001)
  [54]=>
  float(5.9)
  [55]=>
  float(6)
  [56]=>
  float(6.1000000000000005)
  [57]=>
  float(6.2)
  [58]=>
  float(6.300000000000001)
  [59]=>
  float(6.4)
  [60]=>
  float(6.5)
  [61]=>
  float(6.6000000000000005)
  [62]=>
  float(6.7)
  [63]=>
  float(6.800000000000001)
  [64]=>
  float(6.9)
  [65]=>
  float(7)
}
 
WRInaute accro
Heu ? Sur mon serveur (PHP 7.4), var_dump(range(0.5, 7, 0.1));

ça donne :
Code:
array(66) {
  [0]=>
  float(0.5)
  [1]=>
  float(0.6)
  [2]=>
  float(0.7)
  [3]=>
  float(0.8)
  [4]=>
  float(0.9)
  [5]=>
  float(1)
  [6]=>
  float(1.1)
  [7]=>
  float(1.2)
  [8]=>
  float(1.3)
  [9]=>
  float(1.4)
  [10]=>
  float(1.5)
  [11]=>
  float(1.6)
  [12]=>
  float(1.7)
  [13]=>
  float(1.8)
  [14]=>
  float(1.9)
  [15]=>
  float(2)
  [16]=>
  float(2.1)
  [17]=>
  float(2.2)
  [18]=>
  float(2.3)
  [19]=>
  float(2.4)
  [20]=>
  float(2.5)
  [21]=>
  float(2.6)
  [22]=>
  float(2.7)
  [23]=>
  float(2.8)
  [24]=>
  float(2.9)
  [25]=>
  float(3)
  [26]=>
  float(3.1)
  [27]=>
  float(3.2)
  [28]=>
  float(3.3)
  [29]=>
  float(3.4)
  [30]=>
  float(3.5)
  [31]=>
  float(3.6)
  [32]=>
  float(3.7)
  [33]=>
  float(3.8)
  [34]=>
  float(3.9)
  [35]=>
  float(4)
  [36]=>
  float(4.1)
  [37]=>
  float(4.2)
  [38]=>
  float(4.3)
  [39]=>
  float(4.4)
  [40]=>
  float(4.5)
  [41]=>
  float(4.6)
  [42]=>
  float(4.7)
  [43]=>
  float(4.8)
  [44]=>
  float(4.9)
  [45]=>
  float(5)
  [46]=>
  float(5.1)
  [47]=>
  float(5.2)
  [48]=>
  float(5.3)
  [49]=>
  float(5.4)
  [50]=>
  float(5.5)
  [51]=>
  float(5.6)
  [52]=>
  float(5.7)
  [53]=>
  float(5.8)
  [54]=>
  float(5.9)
  [55]=>
  float(6)
  [56]=>
  float(6.1)
  [57]=>
  float(6.2)
  [58]=>
  float(6.3)
  [59]=>
  float(6.4)
  [60]=>
  float(6.5)
  [61]=>
  float(6.6)
  [62]=>
  float(6.7)
  [63]=>
  float(6.8)
  [64]=>
  float(6.9)
  [65]=>
  float(7)
}

Edit: ah oui bizarre en PHP 8 c'est différent : https://3v4l.org/lNrtL
 
WRInaute impliqué
Mais ce n'est que le toString qui fait float(1.2000000000000002)
Apparemment, pas seulement. Avec ce range, j'ai un problème avec 1.7

Si je fais (cf https://3v4l.org/YI9A4):
Code:
foreach (range(1, 2, 0.1) as $r) {
    var_dump($r);
    echo '<br>';
    if ($r === 1.7)
        echo 'ok<br>';
    if (intval($r * 10) === 17)
        echo 'okish<br>';
}

Sur ma machine en PHP8, j'obtiens :
Code:
float(1)
float(1.1)
float(1.2)
float(1.3)
float(1.4)
float(1.5)
float(1.6)
float(1.7000000000000002)
okish
float(1.8)
float(1.9)
float(2)

et sur ma machine en 7.4 :
Code:
float(1)
float(1.1)
float(1.2)
float(1.3)
float(1.4)
float(1.5)
float(1.6)
float(1.7)
okish
float(1.8)
float(1.9)
float(2)

Ni l'une, ni l'autre ne considère que ($r === 1.7), alors même que 7.4 affiche float(1.7) :-/
 
Dernière édition:
WRInaute impliqué
print_r() et var_dump() n'affichent pas pareil en PHP 7.4 ou PHP 8 et je ne trouve pas ou c'est documenté ce qui a changé entre les 2 versions.
https://3v4l.org/4k358
Je ne sais pas non plus mais le var_dump de PHP8 est clairement plus fiable et utile pour débugguer. Les différences entre infos de débug et vraie représentation interne de 7.4, c'est le genre de choses qui peuvent faire perdre du temps (et ses nerfs, au passage).

Si ça se trouve, cette différence entre print_r et var_dump est voulue, mais ça m'étonnerait... ça mériterait peut-être un rapport de bug.
 
Discussions similaires
Haut