For - To : Aufzählung Problem

  • Hey, ich wollte mir eine For To Schleife bauen, die die Zahlen von 1-10 in Step 0.1 untereinander schreibt.

    Merkwürdiger Weise ist das Ergebnis davon anders als erwartet :D

    Ist das bei euch auch so ?!

    Warum ist das so ?

    Code
    For $i = 1 To 10 Step 0.1
        ConsoleWrite($i &@CRLF)
    Next
    Spoiler anzeigen

    1

    1.1

    1.2

    1.3

    1.4

    1.5

    1.6

    1.7

    1.8

    1.9

    2

    2.1

    2.2

    2.3

    2.4

    2.5

    2.6

    2.7

    2.8

    2.9

    3

    3.1

    3.2

    3.3

    3.4

    3.5

    3.6

    3.7

    3.8

    3.9

    4

    4.1

    4.2

    4.3

    4.4

    4.5

    4.6

    4.7

    4.8

    4.9

    5

    5.1

    5.2

    5.3

    5.4

    5.5

    5.6

    5.7

    5.8

    5.9

    6

    6.1

    6.19999999999999

    6.29999999999999

    6.39999999999999

    6.49999999999999

    6.59999999999999

    6.69999999999999

    6.79999999999999

    6.89999999999999

    6.99999999999999

    7.09999999999999

    7.19999999999999

    7.29999999999999

    7.39999999999999

    7.49999999999999

    7.59999999999999

    7.69999999999999

    7.79999999999999

    7.89999999999999

    7.99999999999999

    8.09999999999999

    8.19999999999999

    8.29999999999999

    8.39999999999999

    8.49999999999999

    8.59999999999999

    8.69999999999999

    8.79999999999999

    8.89999999999999

    8.99999999999999

    9.09999999999999

    9.19999999999999

    9.29999999999998

    9.39999999999998

    9.49999999999998

    9.59999999999998

    9.69999999999998

    9.79999999999998

    9.89999999999998

    9.99999999999998

    Code
    For $i = 0 To 10 Step 0.1
        ConsoleWrite($i &@CRLF)
    Next
    Spoiler anzeigen

    0

    0.1

    0.2

    0.3

    0.4

    0.5

    0.6

    0.7

    0.8

    0.9

    1

    1.1

    1.2

    1.3

    1.4

    1.5

    1.6

    1.7

    1.8

    1.9

    2

    2.1

    2.2

    2.3

    2.4

    2.5

    2.6

    2.7

    2.8

    2.9

    3

    3.1

    3.2

    3.3

    3.4

    3.5

    3.6

    3.7

    3.8

    3.9

    4

    4.1

    4.2

    4.3

    4.4

    4.5

    4.6

    4.7

    4.8

    4.9

    5

    5.1

    5.2

    5.3

    5.4

    5.5

    5.6

    5.7

    5.8

    5.9

    5.99999999999999

    6.09999999999999

    6.19999999999999

    6.29999999999999

    6.39999999999999

    6.49999999999999

    6.59999999999999

    6.69999999999999

    6.79999999999999

    6.89999999999999

    6.99999999999999

    7.09999999999999

    7.19999999999999

    7.29999999999999

    7.39999999999999

    7.49999999999999

    7.59999999999999

    7.69999999999999

    7.79999999999999

    7.89999999999999

    7.99999999999999

    8.09999999999999

    8.19999999999999

    8.29999999999999

    8.39999999999999

    8.49999999999999

    8.59999999999999

    8.69999999999999

    8.79999999999998

    8.89999999999998

    8.99999999999998

    9.09999999999998

    9.19999999999998

    9.29999999999998

    9.39999999999998

    9.49999999999998

    9.59999999999998

    9.69999999999998

    9.79999999999998

    9.89999999999998

    9.99999999999998

  • Lanealine 9. Mai 2019 um 20:28

    Hat den Titel des Themas von „For - To loop Problem“ zu „For - To : Aufzählung Problem“ geändert.
  • Oh Gott, IEEE 754 schon wieder.

    tl;dr:

    Computer können effizient Kommazahlen nur bis zu einer bestimmten Genauigkeit darstellen um damit schnell rechnen zu können.

    Intern liegt diese als komplett andere Struktur vor als wir Kommazahlen menschlich interpretieren.

    Da diese Genauigkeit Speicherplatz kostet, werden nur Näherungswerte dargestelllt, um dieses Problem zu beseiten solltest du einfach runden oder

    64-Bit Kommazahlen verwenden bspw. in DllStructs.

  • Ich bin mit der Erläuterung nicht ganz zufrieden.

    Das Problem hier ist nicht dass Gleitkommazahlen nur bis zu einer bestimmten Genauigkeit abgespeichert werden.

    Das Grundproblem ist ein anderes: Es gibt in jedem Zahlensystem Zahlen welche nicht endlich dargestellt werden können.

    1/3 wäre für das Dezimalsystem solch ein Fall (0,3333333333....)

    Und hier ist auch genau das das Problem.

    Die Zahlen werden im Binärsystem gehalten und dort ist 0,1 (also im Zehnersystem) eben eine solche nicht endlich darstellbare Zahl.

    IEEE 754 könnte 1 Milliarde Nachkommastellen speichern können - die Rundungsfehler wären in dem Fall dennoch nicht verschwunden.

    Dahe ist das Problem eben nicht das Datenformat sondern das Zahlensystem.

  • Die Zahlen werden im Binärsystem gehalten und dort ist 0,1 (also im Zehnersystem) eben eine solche nicht endlich darstellbare Zahl

    IEEE 754 könnte 1 Milliarde Nachkommastellen speichern können - die Rundungsfehler wären in dem Fall dennoch nicht verschwunden.

    Dahe ist das Problem eben nicht das Datenformat sondern das Zahlensystem.

    Dann würde er aber in den Schleifeniterationen nicht für die erste Hälfte x.1 anzeigen.

    Der Rundungsfehler existiert nach wie vor, allerdings macht er sich ab einer bestimmten Iteration bemerkbar, da intern gerundet/abgeschnitten wird.

    Dass die Erklärung nicht vollständig ist sollte glaube ich nicht überraschen, immerhin gibt es dafür einen vollständigen IEEE-Standard.

  • Dann würde er aber in den Schleifeniterationen nicht für die erste Hälfte x.1 anzeigen.

    Der Rundungsfehler existiert nach wie vor, allerdings macht er sich ab einer bestimmten Iteration bemerkbar, da intern gerundet/abgeschnitten wird.

    Diese Rundung existiert lediglich auf der Ebene der Stringausgabe von Float-Zahlen durch AutoIt.

    Hierbei wird ab der - ich glaube die 16. war es - Nachkommastelle einfach abgeschnitten.

    Die interne Float-Zahl weist den Fehler jedoch dennoch weiterhin auf - man sieht ihn nur nicht da die Stringausgabe entsprechend gestaltet wurde.

    Kann man leicht nachvollziehen wenn man dieses Verhalten explizit übergeht:

    Code
    For $i = 0 To 10 Step 0.1
        ConsoleWrite(StringFormat("%20.18f\t",$i) & $i & @CRLF)
    Next

    Dass die Erklärung nicht vollständig ist sollte glaube ich nicht überraschen, immerhin gibt es dafür einen vollständigen IEEE-Standard.

    Exakt darauf wollte ich hinaus: Beide Fälle liegen eben genau nicht in der IEEE 754 begründet sondern jeweils eine Ebene davor und eine danach. Der Grund dafür, dass Rundungsfehler bei 0,1 überhaupt auftreten liegen eben nicht in der endlichen Genauigkeit von IEEE 754 begründet sondern im Zahlensystem. Und der Grund warum diese sich in manchen Fällen scheinbar nicht auswirken liegt nicht in der Art der Speicherung wie sie in IEEE 754 definiert ist begründet sondern in der anschließenden Konvertierung der Zahl in einen String. In beiden Fällen ist die IEEE draußen.

    Einmal editiert, zuletzt von AspirinJunkie (10. Mai 2019 um 20:44)

  • Und der Grund warum diese sich in manchen Fällen scheinbar nicht auswirken liegt nicht in der Art der Speicherung wie sie in IEEE 754 definiert ist begründet sondern in der anschließenden Konvertierung der Zahl in einen String.

    ....was ja nichts weiter ist (wie von dir oben beschrieben) als ein "abschneiden" der restlichen Nachkommastellen.

    Was aber das Rechenproblem mit dieser "Zahl" letztendlich auch nicht löst, weil der "normalo"-Programmierer davon ausgeht, dass die Rechenvorschrift ein "richtiges" Ergebnis liefert. Und da liegt das eigentliche Problem. Solange der Prozessor/Controller aka "Chip" auf der Platine die letzte(n) Stelle(n) (ich sag es jetzt mal provokant) nach "gutdünken" vergibt, wird sich daran auch nichts ändern!

    Natürlich macht der Prozessor das, was ihm der Programmierer vorgibt. Aber um das das "richtige" Ergebnis einer Berechnung "vorherzusagen", reicht es eben nicht, freudestrahlend auf F5 zu hauen und davon auszugehen, dass "richtig" gerechnet wurde.

    Denn den Prozessor "richtig" Rechnen zu lassen, hängt von den verschiedensten Faktoren ab. Runden (https://de.wikipedia.org/wiki/IEEE_754#Rundungen) oder das gerne verwendete (De)Normalisieren.

    Da hat man dann schnell mal zwei Handvoll Möglichkeiten, "richtig" zu rechnen, aka "richtige" Ergebnisse zu erhalten.

    Getoppt wird das alles noch, wenn man sich https://de.wikipedia.org/wiki/IEEE_754#…d_Quadratwurzel durchliest.

    Zitat von Auszug aus Wikipedia

    IEEE 754 verlangt von einer (Hardware- oder Software-)Implementierung exakt gerundete Ergebnisse für die Operationen Addition, Subtraktion, Multiplikation und Division zweier Operanden sowie der Operation Quadratwurzel eines Operanden. Das heißt, das ermittelte Ergebnis muss gleich demjenigen sein, das bei einer exakten Ausführung der entsprechenden Operation mit anschließender Rundung entsteht.

    Aha. Wenn sich also alle Beteiligten auf EINE der "richtigen" Möglichkeiten einer Berechnung geeinigt haben, dann ist dieses Ergebnis "richtig"!

    DAS muss man verstanden haben, damit der Satz des TE verständlich wird:

    Merkwürdiger Weise ist das Ergebnis davon anders als erwartet :D

    "ERWARTET" ist nämlich das Stichwort.

    Und das ist auch das, um was sich die IEEE 754 gelinde gesagt herumdrückt.

    Das Ergebnis einer Rechenvorschrift ist genau DANN "richtig", wenn das (von wem auch immer) erwartete Ergebnis eintritt!

    Ergo MUSS man im Vorfeld wissen, was das erwartete Ergebnis sein soll, um die (Prozessor/Software-)Einstellungen so vorzunehmen, dass auch genau dieses Ergebnis nach der Berechnung eintritt.

    Was passiert, wenn man das NICHT tut, damit kann man Webseiten füllen....

    https://www5.in.tum.de/~huckle/bugs.html

    https://www5.in.tum.de/persons/huckle/bugse.html


    Lanealine,

    Programmieren heißt, den Code so "hinzubiegen", dass das vom Programmierer erwartete Ergebnis eintritt. Und möglichst alle Eventualitäten, die dieses erwartete Ergebnis verfälschen könnten, zu erkennen und programmiertechnisch im Vorfeld (!) zu beseitigen....

    Da hilft nur, "wach" zu bleiben und regelmäßig (anderer Leute) "fehlerhaften" Code zu debuggen. Viel Spass dabei! Die "Fehler" (die ja nichts weiter sind, als "andere Erwartungen") so hinzubiegen, dass die "Erwartungen" erfüllt werden, ist die eigentliche Kunst!

  • Getoppt wird das alles noch, wenn man sich https://de.wikipedia.org/wiki/IEEE_754#…d_Quadratwurzel durchliest.

    [... Zitat von Wikipedia...]

    Aha. Wenn sich also alle Beteiligten auf EINE der "richtigen" Möglichkeiten einer Berechnung geeinigt haben, dann ist dieses Ergebnis "richtig"!

    Lies nochmal in Ruhe. Mal in anderen Worten ausgedrückt was damit gemeint ist:

    Float-Zahlen nach IEEE 754 haben eine begrenzte Genauigkeit (diese ist nicht fest sondern differeriert über den darstellbaren Zahlenraum).
    Wenn eine Berechnung jedoch ein mathematisches Ergebnis produzieren würde welches jenseits dieser Genauigkeit liegt, dann muss das Ergebnis korrekt auf die darstellbare Genauigkeit hin gerundet sein. Also wenn z.B. nur 5 Nachkommastellen abgebildet werden können dann muss das Ergebnis so aussehen als hätte man mit mehr als 5 Stellen gerechnet und hätte anschließend korrekt auf 5 Stellen gerundet. Konkreter: Rechenergebnis mit 10 Stellen wäre: 0,1234567890 - dann muss diese Zahl folgendermaßen auf 5 Stellen abgebildet werden: 0,12346 anstatt einfach nur 0,12345 hinzuschreiben. Der Ansatz ist meiner Meinung nach vernünftig (da die numerische Differenz zum korrekten Ergebnis minimal wird) und durch entsprechende Standardisierung auch einheitlich.

  • Ich hatte in Ruhe gelesen^^

    Nun zitiere ich dich:

    dann muss das Ergebnis korrekt auf die darstellbare Genauigkeit hin gerundet sein

    Was soll DAS denn? "Korrekt" ist ja wohl mit mehreren verschiedenen Möglichkeiten einer Rundung schlecht machbar, oder nicht?!

    Und die Anzahl der Stellen ist dabei belanglos, denn das definiere ich ja in meinen Anforderungen.

    Das Problem ist, und das kommt in diesen Beiträgen keineswegs rüber, nicht die Berechnung an sich, sondern die Darstellung der Nachkommazahl.

    Und die hat wiederum mit der Genauigkeit der Berechnung nichts zu tun!

    Beispielsweise rechnet die "alte" FPU in x86-Prozessoren mit 80 Bit Genauigkeit. Dargestellt werden idR Zahlen (nach IEE 754) mit 64 Bit. Man könnte jetzt meinen, egal nach welchem Verfahren gerundet wird, das Ergebnis ist immer "richtig", da der letzte Teil der Nachkommastellen, die 16 Bit Differenz, sowieso abgeschnitten wird. Für eine einmalige Addition/Multiplikation ist das auch wahr, egal welchen Rundungsmodus ich im Prozessor einstelle.

    Also wo liegt das Problem? Das Problem liegt in der Berechnung innerhalb langer Schleifen, in der dieser "Fehler" in jeder Berechnung mitgeschleppt wird. Irgendwann dringt der Fehler in den Bereich der Darstellung vor, wobei die Anzahl der dargestellten Nachkommastellen dabei irrelevant ist.

    Konkreter: Rechenergebnis mit 10 Stellen wäre: 0,1234567890 - dann muss diese Zahl folgendermaßen auf 5 Stellen abgebildet werden: 0,12346 anstatt einfach nur 0,12345 hinzuschreiben

    Soweit richtig. Was dabei aber vergessen wird, ist denn die Berechnung (NICHT die Darstellung!) des Ergebnisses, also in diesem Fall die "Zahl" 0,1234567890 überhaupt "richtig"? Denn je nach Rundungsmodus/Einstellung Prozessor weicht das Ergebnis nach einigen Millionen Iterationen um Faktor X ab. Wenn jetzt nach diesen einigen Millionen Iterationen das Ergebnis bspw. 0,2345678901 lautet, dann ist es völlig unerheblich, ob diese Zahl dann "korrekt" auf 5 Stellen gerundet wurde.

    und durch entsprechende Standardisierung auch einheitlich.

    So muss das auch sein, aber wieso sieht die IEEE dann explizit 3 Rundungsmodi vor? Um doch genau die Freiheit zu geben, diesen "Fehler" (im System) geradezubiegen. Und zwar so, dass das Ergebnis zur Berechnung "passt".

    Das eigentliche Problem ist systemimmanent bei Berechnungen im Binärsystem. "Flippe" ich das letzte Bit der Berechnung oder nicht? Halbe Bits gibt es keine....

    Die von dir angesprochene "Standardisierung" sagt

    Zitat

    Statistisch wird dabei in 50 % der Fälle auf-, in den anderen 50 % der Fälle abgerundet,

    aber laut der Anwendung dieser Statistik haben ein Millionär und ein armes Schwein jeder eine halbe Million!

    Wenn die Raumfähre den Landeplatz auf dem Mars verfehlt, weil Europäer und Amerikaner unterschiedliche Maßsysteme verwenden, dann kann man darüber grinsen, aber wenn das passiert, weil bei der Auswahl des "Rundungsmodus" statt "Richtig" einfach nur "Standard" (in diesem Falle FALSCH, denn das Ziel wurde verfehlt) gesetzt wurde, dann werde ich nachdenklich.


    Es kam schon öfter vor, dass ich diverse Algorithmen in ASM umgesetzt hatte und meine Ergebnisse (mit Standard-Einstellung) von denen diverser Compiler/Prozessoren abwichen. Bei der Analyse des Sourcecodes/Compilereinstellungen stellte ich dann fest, dass die Programmierer sehr wohl die unterschiedlichen Möglichkeiten bspw. beim Runden nutzen (in den Prozessoreinstellungen!) , um das Ergebnis an ihre Erwartungen anzupassen.

    Bei Crosscompilern ist das auch bitter nötig, und wenn das nicht funktioniert, na dann wird eben ein "Emulator" benutzt, der die Berechnungen aka Ergebnisse "richtig" anpasst.

    Um bei der Landefähre zu bleiben: Wird der Landeplatz auf dem Mars erreicht, weil bei der Berechnung alles "richtig" berechnet wurde, dann heißt das noch lange nicht, dass dieses System mit identischen Berechnungen zielgenau auf dem Jupiter (oder einem seiner Monde :o) ) landen würde....

    "Standardisiert" ist immer nur so gut wie das damit erreichte Ergebnis.

  • Müsste man noch anpassen wenn man einen negativen step value hat: