2010-10-03 2 views
7

En tant que partie d'un code de test unitaire que j'écris, j'ai écrit la fonction suivante. Le but est de déterminer si «a» peut être arrondi à «b», indépendamment de la précision de «a» ou de «b».Python: a peut être arrondi à b dans le cas général

def couldRoundTo(a,b): 
    """Can you round a to some number of digits, such that it equals b?""" 
    roundEnd = len(str(b)) 
    if a == b: 
     return True 
    for x in range(0,roundEnd): 
     if round(a,x) == b: 
      return True 
    return False 

Voici quelques sortie de la fonction:

>>> couldRoundTo(3.934567892987, 3.9) 
True 
>>> couldRoundTo(3.934567892987, 3.3) 
False 
>>> couldRoundTo(3.934567892987, 3.93) 
True 
>>> couldRoundTo(3.934567892987, 3.94) 
False 

Pour autant que je peux dire, cela fonctionne. Cependant, j'ai peur de me fier à cela étant donné que je n'ai pas une compréhension parfaite des problèmes concernant la précision en virgule flottante. Quelqu'un pourrait-il me dire si c'est une façon appropriée d'implémenter cette fonction? Si non, comment pourrais-je l'améliorer?

Répondre

3

Quelqu'un pourrait-il me dire si cela est un bon moyen de mettre en œuvre cette fonction?

Cela dépend.La fonction donnée se comportera de manière surprenante si b n'est pas précisément égale à une valeur qui serait normalement obtenue directement à partir de la conversion décimale en binaire-flottante.

Par exemple:

>>> print(0.1, 0.2/2, 0.3/3) 
0.1 0.1 0.1 
>>> couldRoundTo(0.123, 0.1) 
True 
>>> couldRoundTo(0.123, 0.2/2) 
True 
>>> couldRoundTo(0.123, 0.3/3) 
False 

Cela échoue parce que le calcul des résultats 0.3/3 dans une représentation légèrement différente de celle 0.1 et 0.2/2 (et round(0.123, 1)).

Si non, comment pourrais-je l'améliorer?

Règle générale: si votre calcul implique spécifiquement les chiffres décimaux de quelque façon, il suffit d'utiliser Decimal, pour éviter toute la base 2 lossy réimportations.

, Decimal comprend en particulier une aide appelée quantize qui rend ce problème trivialement facile:

from decimal import Decimal 

def roundable(a, b): 
    a = Decimal(str(a)) 
    b = Decimal(str(b)) 
    return a.quantize(b) == b 
+0

J'avais le sentiment que cela avait été implémenté quelque part. J'accepte ceci parce que quantifier fait exactement ce que je cherche. Je vous remercie. – Wilduck

1

Une façon de le faire:

def could_round_to(a, b): 
    (x, y) = map(len, str(b).split('.')) 
    round_format = "%" + "%d.%df"%(x, y) 
    return round_format%a == str(b) 

D'abord, nous prenons le nombre de chiffres avant et après la virgule décimale dans x et y. Ensuite, nous construisons un format tel que %x.yf. Ensuite, nous fournissons a à la chaîne de format.

>>> "%2.2f"%123.1234 
'123.12' 
>>> "%2.2f"%123.1264 
'123.13' 
>>> "%3.2f"%000.001 
'0.00' 

Maintenant, tout ce qui reste est de comparer les chaînes.

+0

+1 pour intelligence et me montrer quelque chose que je ne l'ai pas vu. – Wilduck

0

Le seul point dont j'ai peur est la conversion de chaînes en virgule flottante lors de l'interprétation de littéraux en virgule flottante (comme dans http://docs.python.org/reference/lexical_analysis.html#floating-point-literals). Je ne sais pas s'il existe une garantie qu'un littéral à virgule flottante va évaluer le nombre à virgule flottante le plus proche de la chaîne donnée. Cette section mentionnée est l'endroit dans la spécification où je m'attendrais à une telle garantie. Par exemple, Java est beaucoup plus spécifique sur ce qu'il faut attendre d'un littéral de chaîne. De la documentation Double.valueOf(String):

[...] [l'argument] est considéré comme représentant une valeur décimale exacte de la façon habituelle « notation scientifique informatisée » ou comme une valeur hexadécimale exacte; cette valeur numérique exacte est alors théoriquement converti en un « infiniment précis » valeur binaire qui est ensuite arrondi au type à double par la ronde habituelle à la plus proche de la règle IEEE 754 arithmétique en virgule flottante [...]

À moins que vous ne trouviez une telle garantie nulle part dans la documentation Python, vous pouvez avoir de la chance, car certaines anciennes bibliothèques à virgule flottante (sur lesquelles Python pourrait compter) convertissent une chaîne juste à un nombre à virgule flottante proche, pas au meilleur .

Malheureusement, il me semble que ni round, ni float, ni la spécification pour le point flottant ne vous donne une garantie utilisable.

0

Si votre but est de tester si la fonction round va arrondir à la cible, alors vous avez raison. Dans le cas contraire (quoi d'autre est le but?) Si vous êtes dans le doute, vous devez utiliser le module decimal

Questions connexes