Mod (modulo) macht Mist mit Multiplier

    • Offizieller Beitrag

    Hey,

    Hab folgendes Problem: Benutze ein Modulo um herauszufinden, ob eine Zahl eine Nachkommastelle hat. Falls ja soll die Zahl mit 10 Multipliziert werden und der Multiplikator einen runter gerechnet werden damit später die Zahl wieder als float beschrieben werden kann (iVal * 10 ^ iMul). Funktioniert auch meistens relativ gut. Aber bei paar Zahlen macht der Modulo mist. Es funktioniert zwar auch, gibt mir aber ein viel zu großen übertriebenen Modulo zurück.

    Hier mal ein Beispiel. Kann sich das jemand erklären?!
    Value ist 9.73. Der errechnete Multiplier sollte -2 sein und die Value dann 973.
    Der Multiplier ist bei mir aber -15 und die Value dementsprechend 9.73e+015

    Spoiler anzeigen
    [autoit][/autoit] [autoit][/autoit] [autoit]

    ;~ $iTest =9.75;<--funktioniert
    $iTest =9.73;<--funktioniert zwar auch, aber mit viel zu vielen schritten
    $iMult = 0

    [/autoit] [autoit][/autoit] [autoit]

    While Mod($iTest,1)
    ConsoleWrite($iTest&":"&Mod($iTest,1) & @CRLF);Hier zeigt er bei 973 folgendes an: 1.13686837721616e-013
    $iTest *= 10
    $iMult -= 1
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite(Mod(973,1) & @CRLF);Hier zeigt er bei 973 richtigerweise 0 an?!
    ConsoleWrite("iTest: "&$iTest & @CRLF)
    ConsoleWrite("iMult: "&$iMult & @CRLF)
    ConsoleWrite($iTest*10^$iMult & @CRLF)

    [/autoit]

    Lieben Gruß,
    Spider
    p.s.: Ist das nicht ne schöne Alliteration im Titel? :)


    Edit: Ha.. Hätt ich mal in die Hilfe geschaut. "This function does not guarantee that dividend or divisor can be represented, specifically with floating point numbers."
    Lustig.. Das heißt es gibt die Funktion, aber es wird nicht garantiert das sie auch Funktioniert... Nagut, dann muss ichs halt doch alternativ so machen:

    Spoiler anzeigen
    [autoit]


    $iTest =9.73
    $iMult = 0

    [/autoit] [autoit][/autoit] [autoit]

    While Not (Int($iTest) == $iTest);Leider mit doppel =, da wir hier tatsächlich ein String vergleichen müssen. Einfaches = gibt auch ein zu hohen Multiplier aus
    $iTest *= 10
    $iMult -= 1
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite("iTest: "&$iTest & @CRLF)
    ConsoleWrite("iMult: "&$iMult & @CRLF)
    ConsoleWrite($iTest*10^$iMult & @CRLF)

    [/autoit]
  • Führe mal folgendes aus:

    [autoit]

    ConsoleWrite(StringFormat("%20.18f\n", 9.73))

    [/autoit]


    Was sehen wir? - 9,73 ist nicht für den Computer exakt darstellbar.
    Das liegt schlicht am verwendeten Binärsystem.
    Ist das selbe Prinzip wenn man 1/3 im Dezimalsystem darstellen will - geht nicht - es ist eine unendliche Zahl.

    Heißt also: Es liegt nicht an der Funktion Mod sondern am Binärsystem (ist auch egal ob man Gleitkommazahlen oder Festkommazahlen verwendet)
    Nun wird auch klar warum der Stringvergleich funktioniert: Bei der Umwandlung einer Zahl in einen String rundet AutoIt die Zahl automatisch auf 14 Nachkommastellen (im Dezimalsystem).
    Das entfernt den entsprechenden binären Rundungsfehler.

    Wenn du wissen willst ab welcher Stelle der Fehler ungefähr auftritt - hier eine kleine Funktion:

    Spoiler anzeigen
    [autoit]


    ConsoleWrite(_GET_FLT_EPSILON() & @CRLF)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION#=================================================================================
    ; Name...........: _GET_FLT_EPSILON
    ; Description ...: Berechnet den binären Rundungsfehler
    ; Syntax.........: _GET_FLT_EPSILON
    ; Return values .: Epsilon
    ; Author ........: AspirinJunkie
    ;============================================================================================
    Func _GET_FLT_EPSILON()
    Local $x = 1
    Do
    $x /= 2
    Until 1 + $x <= 1
    Return $x
    EndFunc ;==>_GET_FLT_EPSILON

    [/autoit]
    • Offizieller Beitrag


    Wieder ein bisschen schlauer, danke dir! Wobei ich glaube, dass der Stringvergleich nichts mit Rundungen zu tun hat, da wird doch einfach nur geschaut, ob exakt die gleichen ASCII Zeichen dort stehen.

    Gruß,
    Spider

  • Der Vergleich an sich hat nichts damit zu tun.
    Das ist richtig.
    Allerdings wird die Zahl vorher implizit in einen String gewandelt und dabei auf 14 Stellen gerundet.

    Hier mal ein Beispiel zur Verdeutlichung:

    [autoit]

    ConsoleWrite("--- Zahlenvergleich mit 15 bzw. 14 Nachkommastellen:" & @CRLF)
    ConsoleWrite("1 = 1.000000000000001: " & @TAB & (1 = 1.000000000000001) & @CRLF)
    ConsoleWrite("1 = 1.00000000000001: " & @TAB & (1 = 1.00000000000001) & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ConsoleWrite("--- Stringvergleich mit 15 bzw. 14 Nachkommastellen:" & @CRLF)
    ConsoleWrite("1 == 1.000000000000001: " & @TAB & (1 == 1.000000000000001) & @CRLF)
    ConsoleWrite("1 == 1.00000000000001: " & @TAB & (1 == 1.00000000000001) & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ConsoleWrite("--- Stringumwandlung der Zahl mit 15 bzw. 14 Nachkommastellen:" & @CRLF)
    ConsoleWrite("String(1.000000000000001): " & @TAB & String(1.000000000000001) & @CRLF)
    ConsoleWrite("String(1.00000000000001): " & @TAB & String(1.00000000000001) & @CRLF)

    [/autoit]