Rundung nach IEEE 754 Standard



  • Hallo!

    Ich berechne Lösungen zu linearen Gleichungssystemen. Ich arbeite mit Visual Studio, der Algorithmus ist in C geschrieben. Meine Frage:

    Wie rundet der Rechner (Default Einstellung) die binären Zahlen, wenn das Ergebnis kein Teil des Zahlensystems ist (ich nutze double) oder die Mantissenlänge einfach nicht mehr ausreicht (Rechnen mit begrenzter Stellenanzahl)?

    Ich kann das anhand der ausgegebenen Ergebnisse von Rechenoperationen mit Beispielzahlen nicht nachvollziehen. Vielleicht ist das auch nicht ohne weiteres möglich, da die Zahlen wieder in das Dezimalsystem übertragen werden für die Ausgabe?!

    Beispiel:

    a =0.123123123123123123123
    b =0.123123123123123123123
    a + b =0.246246246246246247579847477027215063571929932

    Nach 16 Stellen Genauigkeit, die soweit ich verstanden habe beim double Format garantiert sind, erscheinen weitere Zahlen. Welche Bedeutung haben diese?

    Danke!


  • Mod

    Es wird auf die nächstgelegene darstellbare Zahl gerundet. Von dieser Zahl mögen zwar nur ca. 7 (float) bzw. 15 (double) Dezimalstellen relevant sein, das heißt aber nicht zwangsläufig, dass die Zahl nur 7 oder 15 Dezimalstellen hätte. Sie kann viel mehr Stellen haben, aber die zweitnächstgelegene darstellbare Zahl würde sich dann spätestens in der 8. bzw. 16 Stelle unterscheiden.

    PS: Genaugenommen sind die Regeln etwas komplizierter (z.B. was man bei Unentschieden macht) und der Standard gibt auch mehrere mögliche Optionen vor. Das ist für deine Frage aber nicht relevant, ich erwähne es nur als Ausblick über den Tellerrand und zur Ruhigstellung der unvermeidlichen Klugscheißer die ansonsten ankämen.

    PPS: 0.123123123123123123123 ist in double in Wirklichkeit 0.12312312424182891845703125. Das mal zwei ist exakt gerechnet 0.2462462484836578369140625. Und das ist logischerweise auch exakt darstellbar, da eine Multiplikation mit 2 im Binärsystem bloß eine Stellenverschiebung ist. Wie du auf 0.246246246246246247579847477027215063571929932 kann ich nicht nachvollziehen. Kannst du den konkreten Code zeigen?



  • IEEE garantiert eine mindestgenauigkeit pro operation, das resultat darf maximal 0.5 auf der letzten stelle abweichen, was bedeutet, dass es korrekt gerundet sein muss bis einschliesslich dem letzten bit.
    Manche Hardware bzw. sogar tool chain compiliert weniger genaue operationen aus um schneller zu sein, z.b. runden mit 2 bit (un)genauigkeit, gerade was komplexere befehle angeht wie sinus/cosinus oder aber auch division.
    Manche Hardware ist auch genauer, weil die darstellung intern nicht 65 bit ist, sondern z.b. 80bit, was auch bedeutet, dass je nach kompilierung, das resultat genauer oder ungenauer ist (aber innerhalb der IEEE spezifikationen), wenn du mehr als eine operation ausfuehrst.



  • Und in dem Falle, wo das Auf- und Abrunden einen gleich großen Fehler machen würde, rundet man typischerweise so, dass das letzte Bit der Mantisse hinterher 0 ist. Das heißt "round half to even".

    https://en.wikipedia.org/wiki/Rounding#Round_half_to_even



  • SeppJ schrieb:

    Es wird auf die nächstgelegene darstellbare Zahl gerundet. Von dieser Zahl mögen zwar nur ca. 7 (float) bzw. 15 (double) Dezimalstellen relevant sein, das heißt aber nicht zwangsläufig, dass die Zahl nur 7 oder 15 Dezimalstellen hätte. Sie kann viel mehr Stellen haben, aber die zweitnächstgelegene darstellbare Zahl würde sich dann spätestens in der 8. bzw. 16 Stelle unterscheiden.

    PS: Genaugenommen sind die Regeln etwas komplizierter (z.B. was man bei Unentschieden macht) und der Standard gibt auch mehrere mögliche Optionen vor. Das ist für deine Frage aber nicht relevant, ich erwähne es nur als Ausblick über den Tellerrand und zur Ruhigstellung der unvermeidlichen Klugscheißer die ansonsten ankämen.

    PPS: 0.123123123123123123123 ist in double in Wirklichkeit 0.12312312424182891845703125. Das mal zwei ist exakt gerechnet 0.2462462484836578369140625. Und das ist logischerweise auch exakt darstellbar, da eine Multiplikation mit 2 im Binärsystem bloß eine Stellenverschiebung ist. Wie du auf 0.246246246246246247579847477027215063571929932 kann ich nicht nachvollziehen. Kannst du den konkreten Code zeigen?

    SeppJ schrieb:

    Es wird auf die nächstgelegene darstellbare Zahl gerundet. Von dieser Zahl mögen zwar nur ca. 7 (float) bzw. 15 (double) Dezimalstellen relevant sein, das heißt aber nicht zwangsläufig, dass die Zahl nur 7 oder 15 Dezimalstellen hätte. Sie kann viel mehr Stellen haben, aber die zweitnächstgelegene darstellbare Zahl würde sich dann spätestens in der 8. bzw. 16 Stelle unterscheiden.

    PS: Genaugenommen sind die Regeln etwas komplizierter (z.B. was man bei Unentschieden macht) und der Standard gibt auch mehrere mögliche Optionen vor. Das ist für deine Frage aber nicht relevant, ich erwähne es nur als Ausblick über den Tellerrand und zur Ruhigstellung der unvermeidlichen Klugscheißer die ansonsten ankämen.

    PPS: 0.123123123123123123123 ist in double in Wirklichkeit 0.12312312424182891845703125. Das mal zwei ist exakt gerechnet 0.2462462484836578369140625. Und das ist logischerweise auch exakt darstellbar, da eine Multiplikation mit 2 im Binärsystem bloß eine Stellenverschiebung ist. Wie du auf 0.246246246246246247579847477027215063571929932 kann ich nicht nachvollziehen. Kannst du den konkreten Code zeigen?

    Danke für die Antworten!

    Noch mal mein Test:
    a =0.123123123123123123123 (0.7)
    b =0.123123123123123123123 (0.8)
    a + b =0.246246246246246247579847477027215063571929932 (0.9)
    a − b =0.000000000000000000000000000000000000000000000 (0.10)
    a · b =0.015159303447591735217181430073196679586544633 (0.11)
    a/b =1.000000000000000000000000000000000000000000000 (0.12)

    Der Code:
    #include "stdafx.h"
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <stdlib.h>
    #include<time.h>

    int main()
    {
    int i;
    double a, b;
    printf("a eingeben!\n");
    scanf("%lf", &a);

    printf("b eingeben!\n");
    scanf("%lf", &b);

    printf("\na + b");
    printf("\n% 50.45f", a + b);

    printf("\na - b");
    printf("\n% 50.45f", a - b);

    printf("\na * b");
    printf("\n% 50.45f", a * b);

    printf("\na / b");
    printf("\n% 50.45f", a / b);

    if (a > b)
    printf("\nist groesser");

    printf("\nZum Beenden 1 druecken und Enter.\n");
    scanf("%d", &i);
    if (i == 1)
    return 0;

    return 0;
    }

    Das ist schon alles. Mich würde interessieren, wo bei der Addition und der Multiplikation die vielen Stellen herkommen. Ich dachte, Rechnen mit begrenzter Auflösung bzw. Mantissenlänge bedeutet: Menge der darstellbaren (berechenbaren Zahlen) zwischen beispielsweise 2^2 und 2^3 (oder 2^(-8) und 2^(-9))im Binärsystem ergibt sich aus der Mantissenlänge von Typ double (53 oder) so. Also 2^53 Zahlen. Habe gelesen, das entspricht dann einer Mantissenlänge von 16-17 im Dezimalsystem. Wie kann ich im Ergebnis (die Dezimalzahlen oben) dann mehr als
    16-17 signifikante Stellen haben? Haben die ganzen hinteren Stellen noch eine Bedeutung?


  • Mod

    stekuntze schrieb:

    Habe gelesen, das entspricht dann einer Mantissenlänge von 16-17 im Dezimalsystem. Wie kann ich im Ergebnis (die Dezimalzahlen oben) dann mehr als
    16-17 signifikante Stellen haben? Haben die ganzen hinteren Stellen noch eine Bedeutung?

    Habe ich doch schon erklärt. Vielleicht mal mit Beispiel im Dreiersystem statt im Binärsystem: Wenn eine Stelle hinterm Komma im Dreiersystem hast, dann hast du zwischen 0 und 1 die möglichen Werte 0, 1/3, 2/3 und 1. Das ist insgesamt sogar weniger Genauigkeit als im Dezimalsystem, wo du mit einer Stelle 10 verschiedene Werte hättest. Eine Zahl mit einer Genauigkeit von einem Dreier"bit" hat also weniger als eine Dezimalstelle Genauigkeit. Trotzdem hat 1/3 im Dezimalsystem weit mehr als eine Stelle, unendlich viele sogar.

    Wenn ich dir sage, dass eine bestimmte Dreierzahl ungefähr 0.35 wäre, dann weißt du sofort, dass 1/3 gemeint ist. Meine Angabe war viel genauer als nötig war, hätte ich 0.34 gesagt, wärst du auch auf 0.3 gekommen. Es hätte auch 0.3 gereicht.

    Äquivalent mit den 53 Binär"bits" des doubles. Es kann gut sein, dass eine solche Zahl weit mehr als 15 Dezimalstellen hat, wenn man sie ganz exakt hinschreibt. Aber wenn ich dir eine Dezimalzahl auf 15 Stellen genau nenne, wirst du exakt sagen können, welchen double ich damit meine, der Rest ist unnötig.

    Machen wir das gleiche Beispiel noch einmal im Binärsystem mit 4 Bits, das sind log_10(2^4)=1.204... Stellen Genauigkeit im Dezimalsystem. Mögliche Werte zwischen 0 und 1 wären: 0, 0.0625, 0.125, 0.1875, ..., 0.9375, 1. Also 16 verschiedene Werte. Ich kann jeden davon eindeutig mit einer zweistelligen Dezimalzahl beschreiben (ich käme sogar mit ein bisschen weniger aus):
    0.00, 0.06, 0.12, 0.18, ..., 0.93, 1.00. Und das obwohl diese Werte bis zu 4 Nachkommastellen im Dezimalsystem haben! Die vier Stellen bedeuten in diesem Fall nämlich nicht, dass die Werte auf vier Stellen genau wären.

    Verstanden?

    (Sind es bei double eigentlich mindestens 15 oder maximal 16 Stellen Genauigkeit? Ich vergesse es immer und bin zu faul zum Nachrechnen. Ist aber auch nicht so wichtig. Eventuell hatte ich in der Diskussion oben an einigen Stellen 16 sagen müssen, um mathematisch korrekt zu sein)


  • Mod

    nur so eine Nebenfrage: gibt es besser visualisierende Zahlenbeispiele?

    (

    Hugs> 0.123123123123123123123 + 0.123123123123123123123 
    0.246246246246246 :: Double
    Hugs> 0.111111111111 * 0.1111111111
    0.0123456790110988 :: Double
    Hugs> 0.11111111111111111111 * 0.11111111111111111111 
    0.0123456790123457 :: Double
    Hugs> 11111111111111111111 * 11111111111111111111
    123456790123456790120987654320987654321 :: Integer
    

    )



  • SeppJ schrieb:

    Verstanden?

    Ich glaube (hoffe):-)

    Noch mal ein Beispiel auf deinem aufbauend:

    Addition zweier Zahlen zwischen 0 und 1 (0.1234 und 0.2100) und ich tue mal so, als ob der Rechner in der binären Zahlenmenge 4 Stellen hinter der Null zur Verfügung hat.

    a=0.1234 (dezimal...)
    suche nach "nähester" Zahl im binären ergibt:
    a=0.0010 (binär) entspricht 0.1250 im Dezimalsystem

    b=0.2100
    suche nach "nähester" Zahl im binären ergibt:
    b=0.0011 entspricht 0.1875 im Dez.-system (näher als 0.25...)

    a+b=0.0101

    entspricht 0.3125 im Dez.-system
    exakt wäre im Dez.-system 0.3334

    Ich bräuchte für die auf 4 NK-Stellen korrekte Darstellung der Dezimalbrüche a u b also aufgrund von log_10(2^14) = 4.21, 14 NK-Stellen im binären, um die Zahlen exakt abzubilden?

    Wäre das Ergebnis der Addition dann auf jeden Fall 0.3334, evtl. ergänzt um irgendwelche Zahlen, die aus der genauen Übersetzung der Binärzahl kommen, aber für mich keine Bedeutung haben, da sie mit meiner eigentlichen Rechenaufgabe nichts zu tun hatten?

    Grüße!


  • Mod

    Exakt wirst du sowieso nie alle Zahlen des Dezimalsystems mit einer endlichen Anzahl Binärstellen darstellen können, da die Basis des Dezimalsystem 2 und 5 als Primfaktoren hat, die Basis des Binärsystems aber nur 2. Als ganz einfaches Beispiel ist 0.1 (dezimal) im Binärsystem eine periodische Zahl. Analog wie beispielsweise die Zahl 0.1 des Dreiersystem (also 1/3) im Dezimalsystem nur als 0.3(periode) dargestellt werden kann, weil dem Dezimalsystem der Primfaktor 3 fehlt.


Log in to reply