Merkwürdiger Rechenfehler

  • Hallo, ich habe einen merkwürdigen Rechenfehler bei AutoIt gefunden. Weiß jedoch nicht weshalb dieser verursacht wird. Diesen Rechenschritt wird innerhalb eines Algorithmus bei mir ausgeführt:

    [autoit]

    $tInt = DllStructCreate('UINT64 val')
    $tInt.val = 1234567890 * 1e9 + 123456789
    ConsoleWrite($tInt.val & @CRLF)

    [/autoit]

    Ich bekomme da als Ergebnis 1234567890123456769 heraus, jedoch müsste dies 1234567890123456789 lauten. Die vorletzte Ziffer ist also falsch. Weiß einer warum das so ist?

    Einmal editiert, zuletzt von Yjuq (5. Oktober 2014 um 20:25)

  • 1e9 wird implizit ein Double-Wert.
    Also eine Gleitkommazahl.
    Wird nun ein Integer mit einer Gleitkommazahl multipliziert und addiert wird der Integer implizit in ein Double gecastet.
    Das führt dazu, dass dein Ausdruck

    [autoit]

    1234567890 * 1e9 + 123456789

    [/autoit]

    als Double-Wert zwischengespeichert wird bevor du es dem UInt64 zuweist.
    Heißt aber auch: Nun schlägt der binäre Rundungsfehler zu - 1234567890123456789 ist als Double-Wert nicht darstellbar.

    Schreib daher stattdessen:

    [autoit]

    1234567890 * 10^9 + 123456789

    [/autoit]

    Dann bleibt dein Ausdruck auch ein Integer.
    Allerdings ein Signed Int64.
    Deswegen kannst du, auch wenn in deiner Struktur UINT64 steht, mit einem AutoIt-Ausdruck keine Zahlen größer 9223372036854775807 zuweisen obwohl UINT64 dies ermöglichen würde.

  • Das finde ich interessanter^^

    [autoit]

    For $i = 1 To 64
    $iVal = 2 ^ $i
    ConsoleWrite($i & " " & VarGetType($iVal) & " = " & $iVal & @CRLF)
    next

    [/autoit]
  • eukalyptus
    1e9 ist eine Zahl.
    ^ dagegen ist ein Rechenoperator.
    Genauer gesagt ein binärer Operator - also mit zwei Operanden (einer vor dem Operator und einer nach dem Operator).

    ^ verhält sich dann wie jeder andere Operator auch. Der Datentyp seines Rechenergebnisses ist abhängig davon, welchen Datentyp die Operanden haben.
    Da die übergebenen Operanden 2 und 32 beides Integerzahlen sind, ist auch das Ergebnis ein Integer.
    Erst wenn einer der Operanden eine Gleitkommazahl ist, ist das Ergebnis auch eine Gleitkommazahl.
    Ist bei +, -, * ja auch nicht anders.

    Andy
    Cool AutoIt ist clever :)
    Der checkt, dass der Integerwertebereich überschritten wird und switcht daher um auf Double um diese Grenze zu überwinden.

    Edit: Wobei - ne. Der switcht schon vorher. Wirklich interessant.

  • Zitat

    ne. Der switcht schon vorher. Wirklich interessant.

    Auf den ersten Blick ist das eine BCD-Geschichte 8|
    Da hat sich einer gedacht, dass sowieso nur 16 Stellen relevant sind, egal ob Integer oder Float ;( .
    Bin mal gespannt was passiert, wenn sich das 128-Bit-Format rumspricht 8o

  • eukalyptus
    Achso - hab ich mal wieder falsch verstanden.
    Dein Hinweis war aber echt gut.
    Hab das mit der alten Version mal getestet.
    In 3.3.10.2 wurde es schon angepasst, in 3.3.8.1 noch nicht.
    Interessant daran ist, dass ich im Changelog nichts finden, kann was darauf hinweist oder irgendwie damit zu tun haben könnte.
    Vielleicht ist es ja ein Side-Effect der den AutoIt-Entwicklern auch noch nicht aufgefallen ist.

    Andy
    Für mich sieht es auch so aus, als hätten die eine feste Grenze von 1e15 für den Datentypwechsel eingetragen.
    Ich finde das ziemlich willkürlich. Oder wir übersehen einen anderen Grund.

  • Hab nochmal nachgeschaut, stimmt, die 16 signifikanten Stellen entsprechen exakt "double", 52 Bit Mantisse und 11 Bit Exponent.

  • Das lässt den Schluß zu, dass NUR mit Double gerechnet wird, und einfach die 16 (15 bei Integer wg +- ) Stellen dargestellt werden.
    Integer wird demnach per Stringtrimright(zahl, Nachkommastellen) ermittelt :D
    Wenn man "Balls" hat, kann man das genau so machen !