Erreur calcul d'entier [Résolu]

Signaler
-
 Impec -
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)

10 réponses

Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
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])
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
Bonjour,

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

0.6//0.005=119.0 reste 0.005
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.
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
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:
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.
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
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 ...
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 !
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
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?
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.
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
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.
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
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"?
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.
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
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à.
>
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021

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.