OVH Cloud OVH Cloud

Problème de précision

8 réponses
Avatar
Sebastien Aubry
Bonjour,

Dans un de mes programmes Python, je dois retrouver le nombre
d'intervalles entiers compris entre deux bornes, étant donné un pas.

Les valeurs des bornes min et max sont tapées par l'utilisateur.

Exemple : l'utilisateur tape min : 48.0 ; max : 49.0 ; pas : 0.1
il y a alors 10 intervalles (49.0-48.0) / 0.1

En Python, je fais : int(math.floor((borne_max - borne-min) / pas))

Le problème est qu'à cause de la représentation flottante, ça ne marche
pas toujours :


>>> int((49.15-48.05)/0.1)
11

>>> int((49.05-47.95)/0.1)
10

>>> (49.05-47.95)/0.1
10.999999999999943



Alors qu'on devrait trouver 11 dans les 2 cas.
Y a-t-il une méthode pour "retrouver" l'entier caché derrière un flottant ?
Faut-il que j'adopte une représentation des bornes sous forme d'entiers
? (comme elles sont saisies au clavier, ce serait possible !)

8 réponses

Avatar
Hervé Cauwelier
Le problème est qu'à cause de la représentation flottante, ça ne marche
pas toujours :


Oui, c'est un sujet récurrent...

Depuis Python 2.4, il y a le type Decimal :
http://python.org/doc/2.4/whatsnew/node9.html

Alternativement, tu peux manipuler des nombres en virgule fixe.

--
Hervé Cauwelier
http://www.oursours.net/

Avatar
Sebastien Aubry

Le problème est qu'à cause de la représentation flottante, ça ne
marche pas toujours :



Oui, c'est un sujet récurrent...

Depuis Python 2.4, il y a le type Decimal :
http://python.org/doc/2.4/whatsnew/node9.html

Alternativement, tu peux manipuler des nombres en virgule fixe.


Mon programme doit fonctionner en Python 2.1, donc je ne peux pas
utiliser le type décimal
Peux-tu m'expliquer comment on travaille en virgule fixe ? On est obligé
de repasser en binaire, comme je l'ai vu en faisant quelques
recherches sur Google ? Ca me paraît compliqué, pour un calcul aussi
simple...


Avatar
Hervé Cauwelier
Peux-tu m'expliquer comment on travaille en virgule fixe ? On est obligé
de repasser en binaire, comme je l'ai vu en faisant quelques recherches
sur Google ? Ca me paraît compliqué, pour un calcul aussi simple...


Il faut d'abord se fixer une limite de précision, ensuite décaler la
virgule d'autant pour en faire des entiers. Le problème est de ne pas
trop pousser la précision pour ne pas non plus atteindre la limite des
entiers. En plus en Python 2.1, tu dois jongler avec les entiers et les
longs.

un exemple :

print 1.53583 + 5.43965
6.97548



print 153583l + 543965l
697548








(J'ai volontairement utilisé print pour masquer l'erreur d'arrondi.)

Note bien que les deux flottants ont autant de décimales et les entiers
de chiffres. Toute décimale manquante doit être compensée par le zéro
qu'elle sous-entend une fois sous forme d'entier.

J'ai de sombres idées de passer par une chaîne pour les conversions :

("%0.5f" % 1.5).replace('.', '')
'150000'




pour une précision de cinq décimales, d'appeller long() et de remettre
le point au bon endroit à la fin avant de reconvertir en flottant avec
float(). Si float est trop petit, passer à double().

Bien sûr, c'est très limité, je ne considère que les additions. Il
existe peut-être des modules de math en virgule fixe complets.

Un matheux pourra corriger mes éventuelles inepties.

--
Hervé Cauwelier
http://www.oursours.net/



Avatar
Sebastien Aubry

Peux-tu m'expliquer comment on travaille en virgule fixe ? On est
obligé de repasser en binaire, comme je l'ai vu en faisant quelques
recherches sur Google ? Ca me paraît compliqué, pour un calcul aussi
simple...



Il faut d'abord se fixer une limite de précision, ensuite décaler la
virgule d'autant pour en faire des entiers. Le problème est de ne pas
trop pousser la précision pour ne pas non plus atteindre la limite des
entiers. En plus en Python 2.1, tu dois jongler avec les entiers et les
longs.


Merci. Je m'en suis sorti en faisant :

PRECISION = 6 # On effectue les calculs à 10^-6 près
power = math.pow(10, PRECISION)
return (int(maxi * power) - int(mini * power)) / int(step * power)


Avatar
Christophe
Bonjour,

Dans un de mes programmes Python, je dois retrouver le nombre
d'intervalles entiers compris entre deux bornes, étant donné un pas.

Les valeurs des bornes min et max sont tapées par l'utilisateur.

Exemple : l'utilisateur tape min : 48.0 ; max : 49.0 ; pas : 0.1
il y a alors 10 intervalles (49.0-48.0) / 0.1

En Python, je fais : int(math.floor((borne_max - borne-min) / pas))

Le problème est qu'à cause de la représentation flottante, ça ne marche
pas toujours :


int((49.15-48.05)/0.1)
11




int((49.05-47.95)/0.1)
10




(49.05-47.95)/0.1
10.999999999999943






Alors qu'on devrait trouver 11 dans les 2 cas.
Y a-t-il une méthode pour "retrouver" l'entier caché derrière un flottant ?
Faut-il que j'adopte une représentation des bornes sous forme d'entiers
? (comme elles sont saisies au clavier, ce serait possible !)


Ne fais pas int seul pour passer un flottant en entier dans ce cas. int
fait un arrondi par le bas alors qu'avec un arrondi au plus proche tu
n'aurais eu aucun problème. Essaye int(round((49.05-47.95)/0.1))




Avatar
Sebastien Aubry

Bonjour,

Dans un de mes programmes Python, je dois retrouver le nombre
d'intervalles entiers compris entre deux bornes, étant donné un pas.

Les valeurs des bornes min et max sont tapées par l'utilisateur.

Exemple : l'utilisateur tape min : 48.0 ; max : 49.0 ; pas : 0.1
il y a alors 10 intervalles (49.0-48.0) / 0.1

En Python, je fais : int(math.floor((borne_max - borne-min) / pas))

Le problème est qu'à cause de la représentation flottante, ça ne
marche pas toujours :


int((49.15-48.05)/0.1)
11




int((49.05-47.95)/0.1)
10




(49.05-47.95)/0.1
10.999999999999943






Alors qu'on devrait trouver 11 dans les 2 cas.
Y a-t-il une méthode pour "retrouver" l'entier caché derrière un
flottant ?
Faut-il que j'adopte une représentation des bornes sous forme
d'entiers ? (comme elles sont saisies au clavier, ce serait possible !)



Ne fais pas int seul pour passer un flottant en entier dans ce cas. int
fait un arrondi par le bas alors qu'avec un arrondi au plus proche tu
n'aurais eu aucun problème. Essaye int(round((49.05-47.95)/0.1))


Et non, car je veux le nombre d'intervalles entiers : un quotient de
10.5 doit bien retourner 10.





Avatar
Damien Wyart
* Sebastien Aubry in fr.comp.lang.python:
int(round((49.05-47.95)/0.1))


Et non, car je veux le nombre d'intervalles entiers : un quotient de
10.5 doit bien retourner 10.


Je propose :

int(float("%2.2f"%(49.05-47.95))/0.1)

mais je ne sais pas si c'est robuste (pour les nombres à deux décimales,
cela me semble correct) et s'il y a mieux.

--
DW


Avatar
Christophe


Bonjour,

Dans un de mes programmes Python, je dois retrouver le nombre
d'intervalles entiers compris entre deux bornes, étant donné un pas.

Les valeurs des bornes min et max sont tapées par l'utilisateur.

Exemple : l'utilisateur tape min : 48.0 ; max : 49.0 ; pas : 0.1
il y a alors 10 intervalles (49.0-48.0) / 0.1

En Python, je fais : int(math.floor((borne_max - borne-min) / pas))

Le problème est qu'à cause de la représentation flottante, ça ne
marche pas toujours :


int((49.15-48.05)/0.1)
11




int((49.05-47.95)/0.1)
10




(49.05-47.95)/0.1
10.999999999999943






Alors qu'on devrait trouver 11 dans les 2 cas.
Y a-t-il une méthode pour "retrouver" l'entier caché derrière un
flottant ?
Faut-il que j'adopte une représentation des bornes sous forme
d'entiers ? (comme elles sont saisies au clavier, ce serait possible !)




Ne fais pas int seul pour passer un flottant en entier dans ce cas.
int fait un arrondi par le bas alors qu'avec un arrondi au plus proche
tu n'aurais eu aucun problème. Essaye int(round((49.05-47.95)/0.1))



Et non, car je veux le nombre d'intervalles entiers : un quotient de
10.5 doit bien retourner 10.


Et bien, a moins d'utiliser le module Decimal pour avoir une bonne
précision, il reste une technique pour faire un arrondi à l'inferrieur,
mais avec une petite marge :

int((sup-max)/0.1+0.000001)