Numerische Ungenauigkeiten abschätzen und vergleichen



  • Angenommen man möchte etwas ausrechnen, dann kann man dies oft auf viele verschiedenen Wegen tun. In der Mathematik kommt auch immer das Selbe heraus. Wenn man jedoch mit dem PC rechnet können durch numerischen Ungenauigkeiten leicht verschiedene Ergebnisse herrauskommen.

    Als Beispiel hab ich zwei Funktionen funA und funB die mathematisch das selbe ergeben. Kann man nun sagen, wie sich funA und funB auf dem PC maximal unterscheiden können?

    (a+b)+c vs a+(b+c)
    a*(b+c) vs a*b+a*c
    (a*b)*c vs a*(b*c)
    exp((a+b)+c) vs (exp(a)*exp(b))*exp(c)
    usw.


  • Global Moderator

    Ganz normale Fehlerfortpflanzung ist ein Ansatz, der dir schon einmal sehr viel beantworten wird.



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (alle ISO-Standards) in das Forum Mathematik und Physik verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • SeppJ schrieb:

    Ganz normale Fehlerfortpflanzung ist ein Ansatz, der dir schon einmal sehr viel beantworten wird.

    Ja, aber womit soll ich anfangen? Gibt es da eine Übersicht, wie groß ein Fehler bei einer Summe, einen Produkt usw. auf einer CPU sein kann?




  • Global Moderator

    Woops schrieb:

    SeppJ schrieb:

    Ganz normale Fehlerfortpflanzung ist ein Ansatz, der dir schon einmal sehr viel beantworten wird.

    Ja, aber womit soll ich anfangen? Gibt es da eine Übersicht, wie groß ein Fehler bei einer Summe, einen Produkt usw. auf einer CPU sein kann?

    Wie Fließkommazahlen funktionieren ist exakt dokumentiert und standardisiert. Du kannst dir für jede konkreten Operation ganz genau ausrechnen, was das Ergebnis sein wird und dieses mit reellen Zahlen vergleichen. Das ist natürlich relativ nutzlos, aber du kannst natürlich auch aus der bekannten Genauigkeit der Fließkommawerte pauschale Aussagen darüber treffen, wie groß ein Fehler für eine Operation maximal sein wird, unabhängig von den konkreten Eingabewerten.

    In extremen Fällen gibt es aber einen ganz wichtigen Unterschied zu der üblichen mathematischen Fehlerfortpflanzung: Die Fehler sind nicht zufällig. Bei der üblichen Fehlerfortpflanzung nimmt man an, dass sich die Fehler (innerhalb gewisser Grenzen) gegenseitig wegheben, wenn man ein und den gleichen Fehler oft wiederholt. Wenn du hingegen eine Million mal den gleichen Fließkommafelher machst und diese addierst, kommt eine Million mal der Fehler raus und nicht etwa ein Wert um 0. In solchen Fällen muss man also eher eine Worst-Case Betrachtung machen.

    Ansonsten allgemein zu Fehlerfortpflanzung: Nun, das ist halt Stoff der Universitätsmathematik. Zwar nur Stoff der niederen Semester, aber trotzdem zu komplex, als dass ich das Thema auch nur ansatzweise in einem Forenbeitrag erklären könnte. Und durch die oben erwähnten Verkomplizierungen reicht es nicht einmal, dir einfach nur Formeln hinzuknallen, man muss wirklich selbstständig über jeden konkreten Fall nachdenken.

    Immerhin gibt es ein paar Richtschnüre, wenn es dir nicht um konkrete Vorhersagen geht, sondern um ein paar Regeln, wie man Fehler möglichst vermeiden kann:
    1. Differenzen von ähnlich großen Werten sind das absolute Gift. Unbedingt vermeiden. Da wird der Fehler beliebig groß.
    2. Summen von sehr unterschiedlich großen Werten sind auch zu vermeiden, ganz besonders dann, wenn man sie oft wiederholt. Aber weitaus weniger problematisch als der Punkt 1.
    3. Die ersten beiden kommen aus der allgemeinen mathematischen Fehlerfortpflanzung. Hier noch einer speziell für Fließkommazahlen: Vermeide Fälle, bei denen es wichtig ist, zwischen so etwas wie 1.000000001 und 1.000000002 zu unterscheiden. Transformiere das Problem, so dass es stattdessen auf den Unterschied zwischen 0.000000001 und 0.000000002 ankommt.



  • In der C++ Standardbibliothek gibt es die numeric_limits, darunter z.B.

    std::numeric_limits<double>::max_digits10
    std::numeric_limits<double>::digits10
    

    wobei es dabei glaube ich darum geht zu wissen, wie viele Nachkommastellen gebraucht werden, um eine Fliesskommazahl (in oberen Beispiel von double) im Stellenwertsystem mit Basis 10 auszugeben. Zum Beispiel in Verbindung mit std::setprecision. Dabei gibt es scheinbar zwei "round-trips".

    Edit: Bin kürzlich zu diesem Thema in der Programmbibliothek glm auf ULP (unit in the last place) gestossen. Im Manual von glm hat es weiteren Link.