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

Leonidas hat geschrieben: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.
Ja, ist klar. Aber inwiefern ich durch Dual-Objekte Probleme habe, die eine dual() oder bin()-Funktion nicht mit sich bringt, ist mir nicht klar.

Ein

Code: Alles auswählen

number = int(number)
kann ich natürlich in meinen Entwurf auch einbauen; es "löst" das Problem des Umgangs mit den floats. Aber für sinnvoll halte ich das nicht - habe ich ja in einem früheren Posting schon erläutert. Dann merke ich doch gar nicht, dass ein Dual(13.6) oder bin(13.6) oder wie auch immer gar nicht das macht, was ich vermutlich denke, dass es macht. Aber m.E. ist das auch gar nicht der Punkt.

Ich bin - für mich - immer noch nicht "fertig" mit meinem Entwurf.

Was müsste ich den konkret ändern, um duck-typing zu realisieren?
Geht das nur mit einem komplett anderen Ansatz oder sind es nur ein paar Änderungen an der __init__()-Methode?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

EyDu hat geschrieben:..., aber da ich nicht weiss was pütone vor hat, hoffe ich einfach mal, dass es Sinn ergibt.
Ach, gar nichts Großes. Hin und wieder muss ich mit ein paar Dualzahlen rechnen und außerdem bin ich dabei, nicht nur irgendwie in Python zu programmieren, sondern das auch möglichst "pythonic" zu tun. Da kam das gerade recht, weil es klein und überschaubar ist und man eben auch sinnvoll - ganz sicher nicht zwingend - Klassen/Objekte einsetzen kann.

Ich finde es nett, wenn z.B. sowas geht:

Code: Alles auswählen

>>> from dual import *
>>> a = Dual(23)
>>> b = Dual('110101')
>>> a+b
1001100
>>> a*b
10011000011

@zap zu 2)

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Hier mal der pythonischere Weg der Initialisierung:

Code: Alles auswählen

class Dual(object):

    def __init__(self,value):
        self.value = value

    @classmethod
    def fromstring(cls, value):
        return cls(
            int(value,2))

    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)

    def __rep__(self):
        return "<Dual %s-%d>" % (str(self), self.value)

if __name__ == "__main__":
    z1 = Dual.fromstring("1001")
    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)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

def __repr__(self):
    return "Dual(%s)" % self.value
Dann tut die Methode auch das, wozu sie gedacht ist.
BlackJack

Mein Vorschlag zum Thema:

Code: Alles auswählen

import operator

class Dual(object):
    def __init__(self, value):
        if isinstance(value, basestring):
            value = int(value, 2)
        self.value = int(value)
    
    def __int__(self):
        return self.value
    
    def __str__(self):
        value = int(self)
        sign = '-' if value < 0 else ''
        value = abs(value)
        result = list()
        while value:
            result.append(str(value & 1))
            value >>= 1
        return sign + ''.join(reversed(result))
    
    def __repr__(self):
        return '<Dual %s:%d>' % (self, self)


def make_binary_method(cls, operation):
    def meth(self, other):
        return cls(operation(self.value, int(other)))
    return meth

for operation in ['add', 'sub', 'mul', 'div', 'truediv', 'mod']:
    setattr(Dual,
            '__%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))
    setattr(Dual,
            '__r%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))


def 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)
    print repr(1 + Dual(15))

if __name__ == '__main__':
    main()
Nimmt nicht nur in der `__init__()` alles an, was man nach `int` Konvertieren lässt, sondern lässt sich auch mit solchen Objekten in Rechnungen verwenden. Zum Beispiel mit `int`\s.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@audax
Das ist jetzt mal Code, den auch ich verstehe ... :D

Danke, war wirklich hilfreich. Wirft bei mir natürlich gleich weitere Fragen auf:

Warum

Code: Alles auswählen

@classmethod
def fromstring(cls, value):
    return cls(int(value,2))
statt z.B.

Code: Alles auswählen

@staticmethod
def fromstring(value):
    return Dual(int(value,2))
Und: Wenn das "Python thinking" ist, okay. Macht mich aber ein bisschen traurig, weil ich es viel netter fänd, wenn Dual("1010") ebenso möglich wäre wie Dual(15).

@BlackJack
Danke auch für den Entwurf. Den mittleren Teil - Zeile 26-37 - verstehe ich im Moment noch nicht, aber das ist vorerst nicht weiter schlimm; bei nächster Gelegenheit arbeite ich mich dadurch.

Jetzt bin ich natürlich völlig verwirrt: audax will eine extra Methode zur Erzeugung von Dual-Objekten aus einem "dualen String", du machst es nun an der Stelle so ähnlich wie ich in meinem ersten Entwurf und verwendest auch isinstance() :?:

Was mich immer noch nicht glücklich macht:
Bei audax' Variante führt die Erzeugung eines Dual-Objekts aus einem float zunächst nicht zu einem Fehler; erst beim Rechnen oder Ausgeben kommt die Fehlermeldung.
Bei BlackJacks Variante wird wiederum der float kommentarlos zu einem int und dann weiter verarbeitet.

Richtig toll ist natürlich, dass man mit BlackJacks Version jetzt auch int's mit Dual's verrechnen kann. Das gefällt mir.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

pütone hat geschrieben:

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'
:oops: Hatte nen "dreher" in den Rückgabewerten, habs oben geändert.
BlackJack

@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.

Die Zeilen 26-37 erzeugen die ganzen Rechenmethoden in der Liste programmatisch. Hätte man auch mit "Copy'n'Paste"-Programmierung machen können.

Ich behandle Zeichenketten in der `__init__()` besonders weil das IMHO nicht ganz unpythonisch ist. Man sieht das jedenfalls häufiger. Das ist halt ein "besonderer" Datentyp.

Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.
Dachte, ich hätte es verstanden. Wollte es ausprobieren. Und zwar so:

Code: Alles auswählen

>>> from dual import *
>>> class MyDual(Dual):
...     pass
...
>>> MyDual.classmeth('10101')
<Dual 10101-21>
>>> MyDual.staticmeth('10101')
<Dual 10101-21>
Dann habe ich es doch nicht verstanden.
BlackJack hat geschrieben:Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)
Grausam :x
BlackJack

@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)
Jaaaah! :idea: :idea: :idea:
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

@Blackjack:
Exakt die Lösung ist mir vorhin auch eingefallen :D
Brauchte sowas, um dummy-Klassen für Tests zu generieren. Python ist schon recht flexibel :]

Und...basestring kannte ich noch nicht. Sehr pratkisch!
BlackJack

@rabit: Um noch eine konkrete Antwort auf die Frage nach der Binärdarstellung von 2.35 zu liefern: 10.010110₂, wobei der unterstrichene Teil die Periode ist. Der Dezimalbruch lässt sich binär nicht mit endlichen Stellen darstellen.
Antworten