Decimal Implementierung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Hallo,

ich habe mir eine Klasse geschrieben, die mir das Rechnen mit Gewichten ermöglicht à la 0.2g + 2.1 kg = 2.3kg. Für die Klasse habe ich auch die Rich Comparison Methoden implementiert. Jetzt ist mir bei Vergleichtests aufgefallen, dass Gewichte, die gleich sein sollten, nicht gleich sind. Schuld ist wohl die Floating Point Problematik. Ich konnte das Problem folgendermaßen isolieren:

Code: Alles auswählen

>>> foo = 0.0238
>>> bar = 0.022 + 0.0018
>>> foo == bar
False
Nun kann ich ja mittels Decimal beim Vergleichen ein "richtiges" Ergebnis erhalten:

Code: Alles auswählen

>>> foo = Decimal(str(0.0238))
>>> bar = Decimal(str(0.022)) + Decimal(str(0.0018))
>>> foo == bar
True
Wie kann ich jetzt "Decimal" am besten in meinen bestehenden Code implementieren, um Gewichte vergleichen zu können? Einfach jede Berechnung in Decimal konvertieren? Oder kann man da geschickt etwas mit Vererbung machen, indem man von Decimal erbt? Ungeschickter Weise habe ich den Code leider zu Hause auf meinem Rechner gelassen und kann erst heute abend die Klasse posten. Deshalb aus dem Kopf:

Code: Alles auswählen

class Weight(object):
    def __init__(self, value, unit="g"):
        self.value = value
        self.unit = unit
        self.si_prefix = unit[0:1]
        self.si_prefixes = { 'µ' : 6, 'm': 3, 'k': -3}

    @property
    def base_value(self):
        # so ungefähr ;-)
        # genaueres später
        if self.unit == "g":
            return self.value
        else:
            return self.value * 10**(self.si_prefixes[self.si_prefix]*-1)
Würde es bereits aureichen, self.value als Decimal zu schreiben? Denn man kann ja so weiterrechnen ...

Code: Alles auswählen

>>> bar * 2
Decimal('0.0476')
Danke,

Frank
Trichter
User
Beiträge: 45
Registriert: Montag 20. April 2009, 10:21

Wie es aussieht musst du alle Werte mit denen du rechnest in Decimals umwandeln.
Wenn du mit Decimals und Integer Werten arbeitest kannst du die Typen zwar mischen, aber mit Werten vom Typ float geht das schon nicht mehr.

Code: Alles auswählen

>>> a = d.Decimal("5.3")
>>> a
Decimal("5.3")
>>> a * 10.4
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsupported operand type(s) for *: 'Decimal' and 'float'
>>> 
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

So geht das jetzt:

Code: Alles auswählen

class Weight(object):

    def __init__(self, value, unit="g"):
        self.value = Decimal(str(value))
        self.unit = unit
        self.si_prefix = self.unit[0:1]

        self.si_factors = {
            "n" : Decimal(str(9)),
            "µ" : Decimal(str(6)),
            "m" : Decimal(str(3)),
            "k" : Decimal(str(-3))
        }

    @property
    def base_value(self):
        """convert to gramm"""

        if self.unit == self.si_prefix:
            return self.value
        else:
            return self.value * 10**(self.si_factors[self.si_prefix]
                                     * Decimal(str(-1)))

    def __eq__(self, weight):
        return self.base_value == weight.base_value
erlaubt jetzt auch

Code: Alles auswählen

    bar = Weight(23.8, "mg")
    foo = Weight(1.8, "mg") + Weight(22, "mg")
    print foo == bar
True
Antworten