Erreur calcul d'entier

Résolu/Fermé
Impec - 21 avril 2021 à 16:30
 frv - 24 janv. 2022 à 01:39
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)
A voir également:

11 réponses

yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024 1 481
21 avril 2021 à 23:07
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
Phil_1857 Messages postés 1883 Date d'inscription lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 178
21 avril 2021 à 17:16
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
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
Phil_1857 Messages postés 1883 Date d'inscription lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 178
21 avril 2021 à 20:46
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

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
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
Phil_1857 Messages postés 1883 Date d'inscription lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 178
22 avril 2021 à 12:27
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
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
yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024 1 481
22 avril 2021 à 22:36
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
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
yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024 1 481
23 avril 2021 à 09:27
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
yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024 1 481
23 avril 2021 à 09:37
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
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
yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024 1 481
23 avril 2021 à 09:59
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
Impec > yg_be Messages postés 22779 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 12 mai 2024
23 avril 2021 à 10:03
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
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