Dualzahlenrechner

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.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Ich habe - für den gelegentlichen Eigengebrauch - ein Minimodul geschrieben,
um mal schnell im interaktiven Modus ein paar Dualzahlen zu verrechnen.

Ja, mein Taschenrechner kann das auch (aber nicht mit so großen Zahlen ...) und ganz bestimmt gibt es schon fertige Module, die das können.

Mir geht es darum, ob die Art, wie ich es umgesetzt habe, "Python thinking" ist. Macht der Pythologe an sich das so?

Code: Alles auswählen

# dual.py
# Grundrechenarten mit Dualzahlen

class Dual(object):

    def __init__(self,value):
        self.value = int(value,2) if isinstance(value,str) else value
        if isinstance(self.value,float):
            raise ValueError # float is not supported

    def __str__(self):
        sgn = "-" if self.value<0 else ""
        bin = "" if self.value!=0 else "0"
        dec = abs(self.value)
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __add__(self,bin):
        return Dual(self.value+bin.value)

    def __sub__(self,bin):
        return Dual(self.value-bin.value)

    def __mul__(self,bin):
        return Dual(self.value*bin.value)

    def __div__(self,bin):
        return Dual(self.value//bin.value)

    def __mod__(self,bin):
        return Dual(self.value%bin.value)

if __name__ == "__main__":
    z1 = Dual(13)
    z2 = Dual(5)
    print "%s + %s = %s" %(z1,z2,z1+z2)
    print "%s - %s = %s" %(z1,z2,z1-z2)
    print "%s * %s = %s" %(z1,z2,z1*z2)
    print "%s / %s = %s" %(z1,z2,z1/z2)
    print "%s %% %s = %s" %(z1,z2,z1%z2)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Also die ganzen isinstance sind bestimmt nicht "Python thinking". Was passiert wenn dein übergebenens Objekt weder ein int noch ein float noch ein String ist? Z.B. ein unicode-Objekt. Schreib doch einfach nur self.value = int(value) und fertig.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Könnte man nicht irgendwie `int` subclassen und __str__ und __unicode__ überschreiben?

Ich knabber nur gerade daran, wie ich dem das beibringen kann...

Code: Alles auswählen

class Dual(int):
    def __new__(cls, arg="0"):
        it = int.__new__(cls, int(arg, 2))
Tja. Und nun? :oops:

€dit:

Code: Alles auswählen

class Dual(int):
    def __new__(cls, arg="0"):
        it = int.__new__(cls, int(arg, 2))
        return it

    def __str__(self):
        if self > 0:
            sgn = "+"
        else:
            sgn = "-"
        dec = abs(self)
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __unicode__(self):
        return str(self)
Nur...wie bringe ich ihn nun dazu, beim addieren wieder Dual-Objekte zurückzugeben? Jede einzelne Methode überschreiben? :(
Zuletzt geändert von audax am Mittwoch 12. März 2008, 23:41, insgesamt 1-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

audax hat geschrieben:Könnte man nicht irgendwie `int` subclassen und __str__ und __unicode__ überschreiben?
Um was zu erreichen? Natürlich, es ist schon möglich.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Damit man eben eine hübsche Binärdarstellung bekommt....

Ich knabber nur an den Typen der Returnwerten :|

Grml:

Code: Alles auswählen

class Dual(int):
    __slots__ = []

    def __new__(cls, arg="0"):
        if isinstance(arg, int):
            value = arg
        else:
            value = int(arg, 2)
        return int.__new__(cls, value)

    def __str__(self):
        if self > 0:
            sgn = "+"
        else:
            sgn = "-"
        dec = abs(self)
        bin = ""
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __unicode__(self):
        return str(self)

    def __repr__(self):
        return str(self)

Code: Alles auswählen

In [33]: d.Dual(d.Dual("10011") + d.Dual("1100"))
Out[33]: +11111
Man muss es eben noch in ein neues Object packen :/
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

audax hat geschrieben:Damit man eben eine hübsche Binärdarstellung bekommt....
Ich habe mal BlackJacks Code ausgeliehen:

Code: Alles auswählen

>>> def int2bin(value):
...     result = list()
...     while value:
...         result.append(str(value & 1))
...         value >>= 1
...     result.reverse()
...     return ''.join(result).zfill(8)
...
>>> class binaryinteger(int):
...     def __repr__(self):
...         return "binaryinteger(%d)" % self
...     def __str__(self):
...         return int2bin(self)
...     def __unicode__(self):
...         return str(self).decode('ascii')
... 
>>> bi = binaryinteger(10)
>>> bi
binaryinteger(10)
>>> str(bi)
'00001010'
>>> unicode(bi)
u'00001010'
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Ich hab den Code einfach pütone übernommen und nicht großartig weiter angeschaut ;)

Code: Alles auswählen

class Dual(int):
    __slots__ = []

    def __new__(cls, arg="0"):
        if isinstance(arg, int):
            value = arg
        else:
            value = int(arg, 2)
        return int.__new__(cls, value)

    def __str__(self):
        value = int(self)
        result = []
        while value:
            result.append(str(value & 1))
            value >>= 1
        result.reverse()
        return ''.join(result).zfill(8)

    def __unicode__(self):
        return str(self).decode("ascii")

    def __repr__(self):
        return "<Dual %s, %d>" % (self, self)
Bleibt immer noch das Problem der Returnwerte, dass sich wohl so einfach nicht lösen lässt...
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ist zwar nicht annähernd fertig, ausgereift oder getestet, aber hier mal ein möglicher Ansatz zur Lösung der return-Werte:

Code: Alles auswählen

def BinaryMeta(target, *the_types):
    class BinaryMetaClass(type):
        def __new__(cls, name, bases, dct):
            #update methods
            for base in bases:
                for name, func in base.__dict__.items():
                    if not name in dct:
                        dct[name] = BinaryMetaClass.create_caller(func)
            return type.__new__(cls, name, bases, dct)
        
        @staticmethod
        def has_given_type(result):
            return type(result) in the_types

        @staticmethod
        def create_caller(f):
            def caller(*args, **kwds):
                result = f(*args, **kwds)
                if BinaryMetaClass.has_given_type(result):
                    return target(result)
                return result
            
            return caller
                    

    return BinaryMetaClass

class binaryint(int):
    __metaclass__ = BinaryMeta(binaryint, int)
    
    def __repr__(self):
        return "binaryint(%d)" % self

    __str__ = __repr__


class binaryfloat(float):
    __metaclass__ = BinaryMeta(binaryfloat, float)

    def __repr__(self):
        return "binaryfloat(%f)" % self

    __str__ = __repr__


if __name__ == "__main__":
    x = binaryint(10)
    y = binaryint(20)
    z = binaryfloat(4.0)
    print x+y, x-y, x*y, x % y, x**z
    print "!!!", x**2.0, "!!!"
Ergibt:

Code: Alles auswählen

binaryint(30) binaryint(-10) binaryint(200) binaryint(10) binaryfloat(10000.000000)
!!! 100.0 !!!
Edit: Holla, auch Beispielcode mit IDLE entwickeln ist gefährlich :roll: Steckt noch ein Fehlerchen drin, da natürlich "binaryint" und "binaryfloat" zur Erzeugung der Metaklassen noch nicht bekannt sind...
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Ach du meine Güte. Fieser Hack :D

Aber ne gute Idee, so klappt das immerhin schonmal
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

So, nun läuft es wie gewünscht:

Code: Alles auswählen

def BinaryMeta(*the_types):
    class BinaryMetaClass(type):
        def __new__(cls, cname, bases, dct):
            #update methods
            for base in bases:
                for name, func in base.__dict__.items():
                    if not name in dct:
                        dct[name] = BinaryMetaClass.create_caller(func)
            res = type.__new__(cls, cname, bases, dct)
            BinaryMetaClass.target = res
            return res
        
        @staticmethod
        def has_given_type(result):
            return type(result) in the_types

        @staticmethod
        def create_caller(f):
            def caller(*args, **kwds):
                result = f(*args, **kwds)
                if BinaryMetaClass.has_given_type(result):
                    return BinaryMetaClass.target(result)
                return result
            
            return caller
                    
    return BinaryMetaClass


def binary_creator(base, name):
    class binary(base):
        __metaclass__ = BinaryMeta(base)
        
        def __str__(self):
            return "%s(%s)" % (name, base.__str__(self))

    return binary

binaryint = binary_creator(int, "binaryint")
binaryfloat = binary_creator(float, "binaryfloat")


if __name__ == "__main__":
    x = binaryint(10)
    y = binaryint(20)
    z = binaryfloat(4.0)
    print x-y, x*y, x % y, x**z
    print "!!!", x**2.0, "!!!"
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Darii hat geschrieben:Also die ganzen isinstance sind bestimmt nicht "Python thinking".
Damit kann ich nichts anfangen. Was heißt "bestimmt nicht" und vor allem: Warum nicht? Was wäre denn an dieser Stelle "Python thinking"?
Darii hat geschrieben:Was passiert wenn dein übergebenens Objekt weder ein int noch ein float noch ein String ist? Z.B. ein unicode-Objekt.
Tja, dann gibt es wohl irgendwann eine Fehlermeldung. Aber mal ehrlich:
Einen Test auf unicode kann man ja einfach noch ergänzen und alles andere dürfte wohl in der Praxis entfallen.
Darii hat geschrieben:Schreib doch einfach nur self.value = int(value) und fertig.
Das ist aber ganz schlecht. Erstens kann man dann nicht mehr Dual-Objekte durch einen Aufruf von Dual("101101") erzeugen und zweitens werden floats dann einfach zu ints glattgebügelt. Das kann doch nicht die Lösung sein.

@alle anderen:
Ich sehe in der Hauptsache, dass eure Codes immer komplizierter werden, aber ich habe nicht verstanden, was an meinem Code schlecht wäre, dass man es anders machen müsste. Für Hinweise wäre ich dankbar.

Sinnvoll ist auf jeden Fall eine Ergänzung der __repr__()-Methode. Das habe ich inzwischen nachgeholt.

Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
rabit
User
Beiträge: 11
Registriert: Freitag 1. Februar 2008, 15:01

pütone hat geschrieben: Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
Welchen dualen Wert hat den zum Beispiel die Float Zahl von 2,35.
Würde mich mal interessieren. ;)

10, 100011 sicher nicht.

Grüße
rabit
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das Prinzip ist genau so wie bei Dezimalzahlen: Die erste Stelle hinter dem Komma hat die Wertigkeit 10^-1, die zweite 10^-2, usw.

Bezogen auf Binärzahlen gilt als für die erste Nachkommastelle 2^-1, für die zweite 2^-2, usw.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

pütone hat geschrieben:
Darii hat geschrieben:Also die ganzen isinstance sind bestimmt nicht "Python thinking".
Damit kann ich nichts anfangen. Was heißt "bestimmt nicht" und vor allem: Warum nicht? Was wäre denn an dieser Stelle "Python thinking"?
Es ist nicht pythonic, weil es kein duck-typing erlaubt. Wenn es sich wie eine Zahl verhält, dann sollte sie akzeptiert werden, statt nachzuprüfen ob sie von ``int`` erbt. In Python 3 gibt es zusätzlich noch Abstract Base Classes, die einem in der Hinsicht einige weitere Freiheiten bieten.
pütone hat geschrieben:Tja, dann gibt es wohl irgendwann eine Fehlermeldung. Aber mal ehrlich:
Einen Test auf unicode kann man ja einfach noch ergänzen und alles andere dürfte wohl in der Praxis entfallen.
Warum also überhaupt Typen testen?
pütone hat geschrieben:Ich sehe in der Hauptsache, dass eure Codes immer komplizierter werden, aber ich habe nicht verstanden, was an meinem Code schlecht wäre, dass man es anders machen müsste. Für Hinweise wäre ich dankbar.
Ich wundere mich auch, was an meinem ``binaryinteger`` das Problem sein sollte, dass man Metaklassen drauf werfen müsste. Es würde wohl auch reichen ``__add__`` etc. zu wrappen, so dass sie ``binaryinteger`` zurückgeben, damit die operationen die richtigen zahlen ausgeben. Wobei, ich weiß gar nicht ob es so sinnvoll ist, überhaupt ``binaryinteger`` auszugeben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

rabit hat geschrieben:
pütone hat geschrieben: Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
Welchen dualen Wert hat den zum Beispiel die Float Zahl von 2,35.
Würde mich mal interessieren. ;)

10, 100011 sicher nicht.

Grüße
rabit
Wo ist denn hier ein Zusammenhang? Das eine hat doch mit dem anderen gar nichts zu tun. Im übrigen kann man meinem Code entnehmen, dass ich ja gerade keine Fließkommazahlen / Dezimalbrüche in Dualzahlen umwandeln will.

Edit: Die eingebaute Funktion hex() verarbeitet im übrigen auch keine Fließkommazahlen.

Wie ich im 1. Posting schon geschrieben habe, geht es mir nicht darum, dass ich eine andere Funktionalität wollte als der vorgestellte Code es leistet oder unzufrieden wäre mit dem, was er leistet, sondern nur darum, ob die Art und Weise, wie ich es umgesetzt habe, "python thinking" ist oder nicht.
Leonidas hat geschrieben:Warum also überhaupt Typen testen?
In meinem Original-Code verwende ich den Typentest ja in erster Linie dazu, die beiden möglichen Fälle für die Konstruktion eines Dual-Objektes zu unterscheiden, weil ich z.B. auch folgendes haben wollte:

Code: Alles auswählen

 summe = Dual(20) + Dual('101001110')
Das geht ja mit meinem Entwurf.
Leonidas hat geschrieben:Es ist nicht pythonic, weil es kein duck-typing erlaubt.
a) Verstehe ich; b) dann aber auch wieder nicht.

Aus Sicht dessen, der mit Dual() arbeitet, ist es doch duck-typing, oder nicht? Er gibt irgend eine Zahl ein und entsprechend der Art der eingegebenen Zahl wird darauf reagiert. Entweder mit einer entsprechenden Verarbeitung oder mit einem ValueError.

Nach meinem Verständnis passiert doch z.B. etwas ähnliches, wenn ich den Divisionsoperator anwende - je nach Zahlentyp funktioniert er unterschiedlich (ja, ich weiß, das wird künftig anders sein; vielleicht hängt das ja mit dem duck-typing zusammen).

Soll das heißen, dass duck-typing in diesem Fall überhaupt nur realisierbar ist, wenn ich Dual von int ableite?

Tut mir leid, falls ich begriffsstutzig bin, aber verstehen würde ich das schon gerne.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Das ist das selbe wie bei hex. Man braucht lediglich Methoden zum konvertieren und ist IMHO glücklich:

Code: Alles auswählen

def bin(number, signed=False):
    """
    Return binary representation of an integer

    Parameters:
      - number<int>    the number to represent
      - signed<bool>   if true we have signed representation
    """
    number = int(number)
    if not number:
        return "0"
    if not signed:
        number = abs(number)
    # compute the count of needed bits
    bits = int((math.log(abs(number))/math.log(2))+1)
    bin = "".join([ "01"[number>>x & 1] for x in xrange(bits-1,-1,-1)])
    return "%s%s" % ((number < 0 and "1" or "0"), bin) 

# Die andere Richtung:
int(bin(4711), 2)

pütone hat geschrieben: Aus Sicht dessen, der mit Dual() arbeitet, ist es doch duck-typing, oder nicht? Er gibt irgend eine Zahl ein und entsprechend der Art der eingegebenen Zahl wird darauf reagiert. Entweder mit einer entsprechenden Verarbeitung oder mit einem ValueError.
Nein, Ducktyping bedeutet das es einer Klasse/Funktion egal ist von welchem Typ ein übergebenes Objekt ist. Hauptsache es verhält sich so.
In diesem fall sollte das heißen das jedes Objekt akzeptiert werden sollte das die __int__ Methode unterstützt. Egal welches Objekt dahinter steckt und wie komplex es ist.
Zuletzt geändert von Zap am Donnerstag 13. März 2008, 18:08, insgesamt 1-mal geändert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Zap hat geschrieben:Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Diese Argumentation kann ich nicht nachvollziehen.
Zap hat geschrieben:Das ist das selbe wie bei hex. Man braucht lediglich Methoden zum konvertieren und ist IMHO glücklich:
Allerdings nicht mit deinem Code .... der ist nämlich fehlerhaft.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

pütone hat geschrieben:
Zap hat geschrieben:Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Diese Argumentation kann ich nicht nachvollziehen.
Es geht darum, dass es ja immer noch ganze Zahlen sind und keine Binärzahlen. Binärzahlen sind wie Hexadezimal oder Dezimal nur eine Repräsentation von Integern.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Mh..

Zu 1) Da ist vielleicht Geschmacksfrage. Obwohl ein Grund fehlt mir dabei. Aber wie Leonidas schon sagt, sie sind und bleiben integer Werte und ich finde das man auch am besten mit denen Weiterarbeiten sollte.

Zu 2) wäre jetzt schön gewesen wenn du mir diesen Fehler auch bennen könntest. Für den Bereich in den ich den getestet habe funktioniert er.

Code: Alles auswählen

In [2]: for i in xrange(0, 100000):
   ...:     assert i == int(bin(i), 2)
   ...:
   ...:

In [3]:
Die Vorzeichenbehaftete Seite kann bestimmt noch ein paar Tests vertragen aber die Stichproben die ich gemacht hab waren korrekt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Die Darstellung einer Zahl kann sehr wohl einen Unterschied machen: Man bedenke beispielsweise das Bestimmen der Quersumme einer Zahl.

Das rechtfertigt meiner Meinung zwar auch noch keine eigene Klasse, aber da ich nicht weiss was pütone vor hat, hoffe ich einfach mal, dass es Sinn ergibt.
Antworten