Rechenfehler - wie vermeiden ?

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Guten Tag,

    ich habe gerade ein Problem bei der Verrechnung von Integer und Float Zahlen.
    Wenn ich von einer Floatpoint Zahl eine ganzzahlige Integer-Zahl abziehe um den Rest (die Nachkommazahl) als solche zu erhalten passieren immer wieder, aber nicht bei jeder Zahlenkombination komische Dinge. So wird aus 0.4 auf einmal 0.3999999999 usw.!
    Wie kann man das vermeiden ?

    Danke im Voraus.

    es grüßt euer
    Willy

  • WillyR_aus_C Gleitkommazahlen haben eine begrenzte Genauigkeit und so einfache Werte wie 1/10 können nicht exakt repräsentiert werden. Das ist im Binärsystem so ähnlich wie bei 1/3 im Dezimalsystem — das hat unendlich viele Nachkommastellen, die Gleitkommadarstellung die von Rechnern intern verwendet wird, hat aber nur endlich viel Speicher für die Darstellung des Wertes.

    Eine weitere lustige/überraschende Eigenschaft: 0.1 ist geringfügig grösser als 0.1 — wenn man das aber 10 mal addiert, ist das Ergebnis geringfügig kleiner als 1:

    Und das ist kein Problem von Python, das hat man bei Gleitkommatypen in anderen Programmiersprachen auch. Die verwenden heute letztlich alle das IEEE 754 Format für die interne Darstellung, weil Prozessoren die Gleitkommatypen direkt verarbeiten, in der Regel dieses Format verarbeiten. So auch die ARM-Prozessoren in den Raspi's. Ich glaube sogar alle. (Es gibt ARM-Prozessoren die keine Gleitkommaunterstützung haben.)

    In Python könnte man `Decimal`-Objekte aus dem `decimal`-Modul in der Standardbibliothek verwenden. Die sind aber langsamer, weil da dann nicht mehr der Prozessor direkt damit arbeiten kann.

    Aber in der Regel verwendet man Gleitkommazahlen und berücksichtigt die Eigenschaften/Ungenauigkeiten. Wobei man da versuchen kann so lange wie möglich bei ganzen Zahlen zu bleiben, und vor allem darauf achtet das man nicht wiederholt Ungenauigkeiten addiert/subtrahiert. Eine Schleife in 0.1er-Schritten macht man beispielsweise nicht durch wiederholte Addition von 0.1 sondern durch Multiplikation mit der (ganzzahligen) Schrittnummer.

    Lesestoff: https://floating-point-gui.de/

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Hallo,

    In Python könnte man `Decimal`-Objekte aus dem `decimal`-Modul in der Standardbibliothek verwenden

    Dann müsste man die Zahl aber als String übergeben oder gibt es noch eine andere Möglichkeit?

    Python
    from decimal import Decimal
    print(Decimal(1.4) - 1)
    0.3999999999999999111821580300
    print(Decimal(1.4 - 1))
    0.399999999999999911182158029987476766109466552734375
    print(Decimal('1.4') - 1)
    0.4

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • !,4 ist schon eine Zahl die nur Näherungsweise dargestellt werden kann, da kann 'Decimal' nicht wieder eine "genaue" 1,4 daraus machen.

    Ich weis ja nicht was du vor hast, aber vielleicht genügt es auch einfach bei der Ausgabe des letztendlichen Ergebnisses die Zahl zu formatieren? Intern kann ja mit den Kommastellen gerechnet werden. (Also falls das auf deinen Anwendungsfall zutrifft.)

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • WillyR_aus_C Wenn Du mit einem `float` startest, hast Du ja eigentlich schon verloren, dann brauchst Du eigentlich auch kein `str` und `Decimal` mehr, denn beim `str()` geht dann ja schon Information verloren wo die Frage ist ob das wichtig war oder nicht. Denn 1.4 ist ja schon nicht als `float` präzise repräsentierbar, das sind, auch wenn Dir das als 1.4 ausgegeben wird, eigentlich 1.399999999999999911182158029987476766109466552734375. Genauer kommt ein `float` da nicht dran.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Guten Abend,

    Ich weis ja nicht was du vor hast, ....

    Gut dann will ich mal dem Ganzen eine Auflösung geben. Ihr werdet jetzt bestimmt wieder sagen oder feststellen wie Umständlich. Aber so sei es.
    Ich habe 2 Meßwerte aus einer ADC-Messung die die je nach Ergebnis eine auch zum Schluss auf die dritte Nachkommastelle abgeschnittene Float-Point Zahl ergeben. Also alle positiven Zahlen von 0.000 bis 8.400 mit max. drei Nachkommastellen sind möglich. Da ich aus diesen Zahlen für eine Diagrammauswertung diese mit jeweils dem kleinsten Step innerhalb von zwei ineinander verschachtelten For- Schleifen verrechnen möchte, musste ich mir etwas einfallen lassen, wie ich das einer For Schleife beibringen kann, die nur Integer kann.
    Also war mein Ansatz, weil rein über String geht es nicht, oder ich kenne den Weg nicht, dass ich die Anzahl der Nachkommastellen darüber feststelle, dass ich von dem Wert den eigenen Integer-Wert abziehe. Diese in einer While-Schleife solange mit der 10er Potenz multipliziere, bis wieder der Integer gleich dem Float-Wert ist, und ich damit einen Multiplikator erhalte, den ich in der For-Schleife nutzen kann. So das mein kleinster Step in der For-Schleife wieder der Stellen des Nachkomma-Anteils entspricht.

    Einfach mal der verkürzte zusammengefasste Funktionsablauf.
    Es funktioniert jetzt dank eures "Decimal" ;)

    es grüßt euer
    Willy

  • Guten Abend,

    Dann würde ich schlank, da die Anzahl der Nachkommastellen bekannt ist, alle Zahlen in INT wandeln math.ceil(x * 1000) (0 .. 8400) und dann damit rechnen.

    Die Werte sind nicht immer zwanghaft 0 und 8.4 ! Diese sind je nach Meßaufgabe abweichend, und es bringt auch nichts alles bis auf die 1000ender Stelle durchzurechnen bzw darstellen zu wollen. Wenn es ZB nur von 1.2 bis 2.0 geht, dann reichen die Schritte 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9 und 2.0 aus.

    es grüßt euer
    Willy

  • numpy.arange kann mit float umgehen. Dann müsstest du eigentlich nur noch die Nachkommastellen bestimmen um den 'step' anzugeben.

    Kannst ja mal testen, ob dir das hilft:

    Ansonsten schön das alles funktioniert wie es soll.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • WillyR_aus_C Was als erstes auffällt, ist das in der Funktion `stellen()` zwei mal das gleiche gemacht wird und deswegen nummerierte Namen existieren. Das sieht nach einer Funktion aus, die man da gebrauchen könnte.

    Dann sind ``while``-Schleifen die auf einen mehr oder weniger exakten Wert einer Gleitkommazahl prüfen, keine gute Idee. Eben wegen der Ungenauigkeiten stellt sich da die Frage ob die jemals abbrechen muss.

    Ungetestet:

    Python
    def berechne_anzahl_nachkommastellen(wert, max_nachkommastallen):
        for result in range(max_nachkommastallen + 1):
            if wert * 10**result % 1 < 10**-max_nachkommastallen:
                break
        return result

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Hallo WillyR,

    Guten Tag,

    ich habe gerade ein Problem bei der Verrechnung von Integer und Float Zahlen.
    Wenn ich von einer Floatpoint Zahl eine ganzzahlige Integer-Zahl abziehe um den Rest (die Nachkommazahl) als solche zu erhalten passieren immer wieder, aber nicht bei jeder Zahlenkombination komische Dinge. So wird aus 0.4 auf einmal 0.3999999999 usw.!
    Wie kann man das vermeiden ?

    Danke im Voraus.

    ich nutze andere Programmiersprachen - solche mit LIA-Unterstützung. Da ergibt sich so ein Problem erst gar nicht. LIA = Large Integer Arithmetics.

    Dazu gibt es einen Beitrag von mir in diesem Forum (Kettenbrüche) mit einem Programmkonstrukt zum beliebigen Konstruieren von Kettenbrüchen.

    In einem anderen Beispiel habe ich gezeigt, wie man z.B. die Zahl pi auf 5000 Stellen genau berechnen kann. Dann sollte hier auch ein Beispiel herumschwirren, wie man Wurzel(2) auf Tausende von Dezimalstellen genau berechnen kann.

    Ansonsten obliegt es dem Anwendungsprogrammierer, die Rechengenauigkeit seines Systems (Prozessor, Betriebssystem, Programmiersprache, Bibliotheken, ...) mit den gestellten Anforderungen abzugleichen und zu optimieren.

    Zur Optimierung:

    Es ist weiterhin Unfug, in einer Schleife immer wieder den selben Betrag zu addieren / subtrahieren. Genauer wird's, wenn man das Produkt aus Schleifendurchlauf und dem Einzelwert verwendet.

    Zum Abgleichen:

    Wenn ich weiß, dass ich nur eine definierte Anzahl an Stellen benötige und durch Rundungsfehler ein erwarteter Wert wahrscheinlich nicht exakt erreicht werden kann, dann prüfe ich nicht auf Identität sondern auf "innerhalb", z.B. min <= x <= max. min und max sind dann Erwartungswert abzgl. bzw. zzgl. einem kleinen Wert, den ich als Abweichung zu erlauben bereit bin (z.B. ein Zehntel oder Hundertstel der Schrittweite).

    Die größten Fehler machst Du, wenn Du Rechenungenauigkeiten ausschließt und Dich auf ungeprüft auf irgendwas verlässt.

    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!