2010-10-04 16 views
3

Je veux faire « == » comparaison de l'utilisation de l'opérateur approximatif dans mon programme: valeurs à virgule flottante x et y sont égaux (==) sicomparaison approximative en python

abs(x-y)/(0.5(x+y)) < 0.001 

Ce qui est une bonne façon de le faire? Étant donné que float est un type intégré, je ne pense pas que je puisse redéfinir l'opérateur ==, n'est-ce pas?

Notez que je voudrais utiliser d'autres fonctionnalités de float, la seule chose que je voudrais changer est l'opérateur d'égalité.

EDIT:

Merci pour les réponses, et je comprends vos arguments sur la lisibilité et d'autres questions. Cela dit, je préférerais, si possible, continuer à utiliser le type float normal, au lieu d'avoir une nouvelle classe ou une nouvelle fonction de comparaison. Est-il même possible de redéfinir == opérateur pour les flotteurs réguliers?

Mes raisons sont ::

(a) tout le monde en utilisant le programme que je vous écris veut que les flotteurs à comparer cette façon

(b) il n'y a aucun moyen dans le anyone monde voudrait jamais utiliser le défaut == pour les flotteurs .. Pourquoi est-ce même dans la langue ???

(c) Je n'aime pas les mots supplémentaires dans le code; en utilisant évidemment les résultats float existants dans aucun changement dans le code que ce soit

EDIT 2.

Maintenant que je sais que je ne peux pas surcharger l'opérateur == pour flotteur, je dois changer ma question. Il deviendra si différent que je vais faire un nouveau à custom comparison for built-in containers

+1

Découvrez l'impact sur la lisibilité, en particulier pour les autres. –

+2

L'opérateur de surcharge '==' semble mignon, mais l'utilisation d'une fonction séparée pour cela est presque toujours une meilleure idée. – Constantin

+0

J'ai de nombreuses comparaisons de listes de nombres (par exemple) qui deviendraient beaucoup plus alambiquées si je ne pouvais pas redéfinir l'opérateur par défaut == .. – max

Répondre

7

Votre définition a deux problèmes:

  1. manque un *

  2. va tenter de diviser par zéro si x + y == 0.0 (qui couvre un cas éventuellement fréquent x == y == 0.0)

Try ceci à la place:

define approx_Equal(x, y, tolerance=0.001): 
    return abs(x-y) <= 0.5 * tolerance * (x + y) 

Édition: Notez l'utilisation de <= au lieu de < ... nécessaire pour que le boîtier x == y == 0.0 fonctionne correctement.

Je ne voudrais pas essayer de passer outre ==

Edit 2: Vous avez écrit:

il n'y a aucun moyen dans le monde quelqu'un voudrait jamais utiliser la valeur par défaut == pour les flotteurs .. Pourquoi est-ce même dans la langue ???

Pas moyen? Supposons que vous ayez une fonction qui renvoie un flottant, et que vous ayez un brainwave sur un algorithme qui produirait les mêmes réponses plus rapidement et/ou plus élégamment; comment le testez-vous?

+0

Une des raisons de ma question est que je compare souvent des dictionnaires, qui ont des valeurs de différents types, y compris float. L'opérateur standard Python == est parfaitement bien, sauf pour le flottant (qui doit être comparé approximativement).Évidemment, il y a des façons faciles de le faire avec du code supplémentaire, mais j'espérais que remplacer == dans float serait la solution la plus propre et la plus élégante. – max

+0

J'accepte cette réponse parce que gérer une sous-classe flottante personnalisée est terriblement incommode en Python pour des raisons indiquées dans les commentaires de cette solution. Autre que la sous-classe, c'est la première solution correcte. – max

+0

Si vous pouviez surcharger float ==, pour éviter un désastre total, vous auriez besoin de surcharger les 5 autres opérateurs relationnels de manière cohérente. Le remplacement serait effectif pour tout le code de la bibliothèque, ce qui ne l'attendrait pas. Par exemple. le tri d'une liste de flottants pourrait ne pas donner la même réponse. Tout l'enfer peut se déchaîner. L'effort de travailler à travers une preuve qu'il ne causera pas de catastrophe serait une entreprise de grande envergure. –

3

Si vous enveloppez les nombres dans une classe, vous pouvez surcharger « == » avec:

def __eq__(self, x): 
    return abs(x - self.x)/(0.5 * (x + self.x)) < 0.001 

mais vous devez réécrire l'expression

abs(x - self.x) < 0.0005 * (x + self.x)

pour éviter la division zéro.

+0

Les arguments ne semblent pas corrects. http://docs.python.org/reference/datamodel.html#object.__eq__ – livibetter

+0

Assurez-vous de définir aussi __ne__, donc vous n'avez pas à la fois a == b et a! = b true. Et sachez qu'avec __eq__ redéfini comme ci-dessus, vous pouvez avoir à la fois a == b et a

+0

livibetter. Désolé je suis allé chercher l'information de http://docs.python.org/library/operator.html – MdaG

18

Vous pouvez créer une nouvelle classe dérivant du type de flotteur intégré, puis remplacer les opérateurs nécessaires:

class InexactFloat(float): 
    def __eq__(self, other): 
     try: 
      return abs(self.real - other)/(0.5 * (abs(self.real) + abs(other))) < 0.001 
     except ZeroDivisionError: 
      # Could do another inexact comparison here, this is just an example: 
      return self.real == other 

    def __ne__(self, other): 
     return not self.__eq__(other) 

print 5.2 == 5.20000000000001 # False 
print 5.2 != 5.20000000000001 # True 
print InexactFloat(5.2) == InexactFloat(5.20000000000001) # True 
print InexactFloat(5.2) != InexactFloat(5.20000000000001) # False 
print InexactFloat(-5) == -5 # True 

# Works for InexactFloat <-> float comparison 
print 5.0 == InexactFloat(5.0) # True 
print InexactFloat(5.0) == 5.0 # True 

# Zero division case (note how I implemented it above!) 
print InexactFloat(-0.00001) == InexactFloat(0.00001) # False 
print InexactFloat(-0.000000001) == InexactFloat(0.000000001) # False 
print InexactFloat(-5) == InexactFloat(5) # False 

# Unit test for fixed negative numbers problem 
print InexactFloat(-5) == InexactFloat(-10) # False 

Vous pouvez également remplacer les opérateurs comme < = etc.

+0

+1: Je pense que c'est la meilleure idée - créer une nouvelle classe au lieu de remplacer les opérateurs intégrés pour les classes intégrées. Mais vous devriez quand même gérer le 'ZeroDivisionError' si les deux nombres sont ou s'ajoutent à zéro. –

+0

@Tim Pietzcker: Oui, j'ai oublié ce cas. Changé pour coder en conséquence. – AndiDog

+1

@AndiDog: À quelle étape passe-t-il de l'utilisation de float à l'utilisation de cette classe? A-t-il besoin d'enrouler 'InexactFloat()' autour de chaque float? Supposons qu'il ne le fasse pas mais fasse tous ses calculs en flottants et obtient deux valeurs a et b ... a-t-il besoin de faire InexactFloat (a) == InexactFloat (b) 'ou peut-il s'en sortir avec InexactFloat (a) == b'? Si ce dernier, pourquoi a-t-il besoin d'un cours du tout? –