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

Discussion dans 'Développement d'un site Web ou d'une appli mobile' créé par saluts92, 26 Août 2021.

  1. saluts92
    saluts92 WRInaute impliqué
    Inscrit:
    12 Avril 2006
    Messages:
    962
    J'aime reçus:
    42
    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
     
  2. colonies
    colonies WRInaute impliqué
    Inscrit:
    10 Septembre 2006
    Messages:
    611
    J'aime reçus:
    86
    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()).
     
  3. saluts92
    saluts92 WRInaute impliqué
    Inscrit:
    12 Avril 2006
    Messages:
    962
    J'aime reçus:
    42
    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 :
     
  4. colonies
    colonies WRInaute impliqué
    Inscrit:
    10 Septembre 2006
    Messages:
    611
    J'aime reçus:
    86
    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)
     
    saluts92 apprécie ceci.
  5. saluts92
    saluts92 WRInaute impliqué
    Inscrit:
    12 Avril 2006
    Messages:
    962
    J'aime reçus:
    42
    une boucle FOR fais des additions aussi ;-)
    merci pour la démo
     
  6. spout
    spout WRInaute accro
    Inscrit:
    14 Mai 2003
    Messages:
    9 179
    J'aime reçus:
    330
    Avec range() c'est mieux non ?

    PHP:
    <?php
    $poids 
    range(0.570.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>
     
    saluts92 apprécie ceci.
  7. colonies
    colonies WRInaute impliqué
    Inscrit:
    10 Septembre 2006
    Messages:
    611
    J'aime reçus:
    86
    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)
    }
    
     
    spout apprécie ceci.
  8. spout
    spout WRInaute accro
    Inscrit:
    14 Mai 2003
    Messages:
    9 179
    J'aime reçus:
    330
    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
     
  9. spout
    spout WRInaute accro
    Inscrit:
    14 Mai 2003
    Messages:
    9 179
    J'aime reçus:
    330
    Mais ce n'est que le toString (de l'array, pas de la value) qui fait float(1.2000000000000002)

    https://3v4l.org/YIlK2
     
    #9 spout, 27 Août 2021
    Dernière édition: 27 Août 2021
  10. colonies
    colonies WRInaute impliqué
    Inscrit:
    10 Septembre 2006
    Messages:
    611
    J'aime reçus:
    86
    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) :-/
     
    #10 colonies, 27 Août 2021
    Dernière édition: 27 Août 2021
    spout apprécie ceci.
  11. spout
    spout WRInaute accro
    Inscrit:
    14 Mai 2003
    Messages:
    9 179
    J'aime reçus:
    330
    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
     
  12. colonies
    colonies WRInaute impliqué
    Inscrit:
    10 Septembre 2006
    Messages:
    611
    J'aime reçus:
    86
    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.
     
Chargement...
Similar Threads - PHP problème comparaison Forum Date
URL canonique et problème entre / et /index.php Débuter en référencement 20 Janvier 2021
Probleme forum phpbb seo Développement d'un site Web ou d'une appli mobile 30 Septembre 2018
Problème de duplicate content avec des URLs contenants /index.php/ URL Rewriting et .htaccess 14 Mai 2018
Problème hiérarchie de classes php. Développement d'un site Web ou d'une appli mobile 9 Janvier 2018
Problème images en https (forum phpbb) [résolu] Développement d'un site Web ou d'une appli mobile 18 Novembre 2017
PHP -Problème retraitement données Mysql avant envoit dans un tableau array Développement d'un site Web ou d'une appli mobile 28 Décembre 2016
Problème avec l'envoi d'un mail sans contenu php Développement d'un site Web ou d'une appli mobile 7 Septembre 2016
Un problème avec php7 Administration d'un site Web 1 Août 2016
Problème erreur redirection cakephp URL Rewriting et .htaccess 4 Avril 2016
problème PHP/Mysql Développement d'un site Web ou d'une appli mobile 29 Octobre 2015