Erreur calcul d'entier

Résolu
Impec -  
 frv -
Bonjour à tous,

Suite à une erreur de taille de matrice dans numpy, j'ai découvert une faille que je n'arrive pas à expliquer dans un calcul d'entier.
Si je prends le quotient 0.6/0.005, la valeur vaut 120. Mais pour python :
0.6//0.005=119
int(0.6/0.005)=120
J'aurais donc tendance à dire que le résultat de int aurait aussi dû donner 119 si ça avait été un problème de précision numérique.
ce qui est curieux, c'est que si maintenant je fais le quotient 0.6/0.05 alors dans les 2 cas j'obtiens 11 (au lieu de 12).
Quelle en est la raison ? Car cela pose des problèmes de taille de matrice à créer pour une discrétisation (la taille étant créée à partir de la taille d'un intervalle divisée par le pas de discrétisation. Sauf qu'en utilisant // ou int, il y a des cas où la valeur ne tombe pas bien et fait buguer le programme)

11 réponses

  1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     
    bonjour,
    il n'est en effet pas toujours simple d'écrire un programme donnant le résultat espéré.
    et il existe de nombreux programmes, comme le tien, donnant des résultats non fiables.

    si tu sais que le résultat de la division est "mathématiquement" entier (comme dans tous les exemples que tu donnes, alors fais ainsi:
    def calc(x,y):
        print(x,y,round(x/y))
    calc(0.6,0.005)
    calc(0.6,0.05)
    calc(60,0.5)
    calc(0.7,0.005)


    Python ne travaille pas naturellement en base décimale. Quand tu lui fournis un nombre tel que 0.6, il va mémoriser, à sa manière, un nombre le plus proche possible de 0.6. Un peu comme tu mémoriserais un tiers : 0.3333333333
    Ainsi, dès que tu fournis un nombre décimal non entier à Python, tu dois être conscient que ce nombre mémorisé peut être légèrement différent. C'est au moment de faire cela que tu introduis une erreur, amplifiée par les opérations choisies ensuite.

    Tu peux facilement éviter cela en n'utilisant que des entiers: 600/5, 60/5, 600/5, 700/5.

    Ou en utilisant des fractions, ainsi:
    def calc2(x,y):
        print(x,y,int((x[0]*y[1])/(x[1]*y[0])))
    calc2([6,10],[5,1000])
    calc2([6,10],[5,100])
    calc2([60,1],[5,10])
    calc2([7,10],[5,1000])
    
    1
  2. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     
    Bonjour,

    / est le symbole de la division
    // est le symbole de la division entière

    0.6//0.005=119.0 reste 0.005
    0
  3. Impec
     
    Bonjour,
    Hé non, la division entière de 0.06 par 0.005 = 120, reste 0 normalement.

    Donc je comprends pas pourquoi python sort 119 dans ce cas précis.
    D'ailleurs si je pars de 0.7 et pas 0.6, j'ai bien :
    0.07//0.005 = 140 et pas 139 (et 0.07%0.005 = 0)

    Et pour en revenir au cas initial, des cas équivalents mathématiquement :
    6//0.05=119
    60//0.5=120

    Donc ça ressemble à des problèmes de précision numérique mais comme dit plus haut, si je fais int(0.6/0.05) je devrais avoir la même erreur dans ce cas (puisque ça revient à faire la division entière) alors que non.
    0
  4. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     
    ha ?
    bizarre...

    moi, j'ai écrit ce code:

    print('\n0.6//0.005={} reste {:.5f}'.format(0.6//0.005, 0.6%0.005))


    et à l'exécution, ca donne ça:
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. Impec
     
    Oui justement, c'est ce que sort le code, mais mathématique c'est faux. Et comme expliqué plus haut, si on écrit la fraction 60//0.5 (qui est exactement la même chose) le résultat est maintenant bien 120.
    Et si vous faites 0.7//0.005 là vous obtenez bien 140 et non pas 139. Donc en résumé le résultat de cette opération n'est pas fiable.
    0
  7. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     
    Bonjour yg_be,

    Effectivement, c'est bizarre quand même !

    En Python, //, c'est la division entière, ou division euclidienne

    Or, dans mon code posté ci-dessus, il accepte des float comme arguments

    Quand j'essaye de faire ça sur ma calculatrice (j'ai un bouton division entière),

    j'ai bien évidemment une erreur : Error Int, ce qui est logique, elle attend des entiers

    Et quand on fait 30//4, on obtiens 7 reste 2 ...
    0
  8. Impec
     
    Bonjour yg_be,

    Oui j'ai l'habitude de gérer les problèmes de précision numérique. Mon problème là c'est que ça ne semble pas être le cas car quand on fait int(0.6/0.005) on obtient bien 120 et pas 119 alors que si c'était un problème de précision numérique on devrait obtenir 119 (puisque int revient à prendre la partie entière).
    C'est même encore pire que ça, puisque :
    0.6//0.005=119 (faux)
    int(0.6/0.005)=120 (correct)

    0.6//0.05=11 (faux)
    int(0.6/0.05)=11 (faux aussi)

    Donc ça marche un peu quand ça veut quoi, même avec int. Et si je repars sur le cas initial:
    60//0.5=120
    60.0//0.5=120
    6//0.05=119

    C'est quand même surprenant cette histoire !
    0
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      tu n'as en rien tenu compte de ce que j'ai écris le 21 avril 2021 à 23:07.

      si tu es satisfait d'être surpris, alors, surtout, ne change rien. comme tu l'observes, ton code n'est pas fiable.
      si tu veux obtenir un résultat prévisible, alors, adapte ton code.

      si tu n'as pas de question, peux-tu marquer la discussion comme résolue?
      0
  9. Impec
     
    Bonjour,

    Non ça ne répond pas à la question initiale. Si tu es satisfait de répondre à côté alors ne change rien. Mais comme tu l'observes ça ne répond pas à la question. (Moi aussi je peux prendre un ton agressif).

    Bref, je reformule : si c'est un problème de précision numérique, comme tu dis, alors cela signifie que lorsque python fait la division de 0.6 par 0.0005 il obtient quelque chose comme 119.999999999, par ex, ce qui ferait effectivement que dans ce cas en réalisant l'opération 0.6//0.0005 on obtient 119. Sauf que si c'était le cas, alors int(0.6/0.0005) devrait aussi donner 119, alors que ça donne bien 120.
    J'aimerais donc comprendre quelle est la raison réelle qui fait que Python donne 119 dans un cas et 120 dans l'autre, puisque visiblement ce n'est pas un problème numérique. Ou alors si c'en est un, pourquoi il conduit à une erreur dans un cas et pas dans l'autre.

    Quant au code que tu proposes, il fonctionne effectivement si on sait par avance écrire la variable en question sous forme de fraction, ce qui n'est pas mon cas puisque je cherche à découper en tranche un espace de taille quelconque à partir d'un pas de longueur donnée.
    0
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      et le premier code que je propose, il ne te donne pas le résultat attendu?
      tu n'as pas expliqué le résultat attendu dans le cas où l'espace n'était pas un multiple du pas. par exemple si l'espace vaut 3 et le pas vaut 0.4.

      si je comprends bien, tu t'étonnes que, pour certaines valeurs, int(x/y) soit parfois différent de x//y.
      moi, je constate que, pour ces valeurs, la réponse étant indéterminée, Python, en utilisant deux algorithmes différents, arrive à deux résultats différents.
      et je propose d'autres méthodes de calcul pour éviter une réponse indéterminée.
      surtout, éviter comme la peste de tronquer une valeur indéterminée proche d'un entier.
      0
  10. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     
    Tu demandes deux choses: "Quelle en est la raison" et "cela pose des problèmes".
    Cherches-tu une solution à ces problèmes, ou bien, ayant trouvé une solution, veux-tu comprendre "pourquoi"?
    0
  11. Impec
     
    Les 2 mon capitaine :)
    Je ne comprends effectivement pas pourquoi int(x/y) soit parfois différent de x//y. J'aurais aimé comprendre la raison de ce résultat différent (parce que fondamentalement, ces 2 opérations font la même chose, à savoir donner le résultat de la division entière et devrait donc générer les mêmes erreurs).
    Mais je retiens le conseil sur le fait de ne pas tronquer mais plutôt d'arrondir, merci. Je vais qd même vérifier que le calcul avec round reste valable dans le cas où l'espace n'est pas un multiple du pas.
    0
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      tu n'as pas expliqué le résultat attendu dans le cas où l'espace n'était pas un multiple du pas. par exemple si l'espace vaut 3 et le pas vaut 0.4. la suggestion avec round() n'est certainement pas adéquate dans ce cas-là.
      0
      1. Impec > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         
        Oui ça ne marche pas dans ce cas (puisqu'un arrondi supérieur rajoute un point en dehors de l'espace). Mais je vais procéder différemment, je vais plutôt déduire le pas du nombre de points que l'inverse, ça évitera d'être coincé par ces considérations. Je mets le problème comme résolu du coup.
        0
  12. frv
     
    En python,
    //
    est un opérateur sur des nombres entiers de tye (
    int
    ), on ne peut pas prévoir ce qu'il donne sur d'autres nombres mais on pourrais espérer que l'interpréteur lève une erreur en cas d'utilisation sur des types
    float
    .
    Par exemple :
    >>> 0.6//0.005
    119.0
    >>> 600//5
    120
    

    Donc si on veut utiliser une division euclidienne il faut l'effectuer sur des entiers, ce n'est pas une question de précision.
    0