Rundungsfehler
-
Hallo,
was verursacht weniger Rundungsfehler und warum?
double sum = 0; for(int i = 0; i < N; ++i) { sum += data[i] / N; }oder
double sum = 0; for(int i = 0; i < N; ++i) { sum += data[i]; } sum /= N;
-
Kommt drauf an (das ist das Beste, was ich dazu sagen kann).
-Welchen wert hat N (power of two?)
-variieren die Werte von data[i] stark oder sind die alle in einem "Bereich"usw....
-
Suchst du vielleicht das?
http://en.wikipedia.org/wiki/Kahan_summation_algorithm
-
im konkreten Fall ist N = 7291
die werte in data sind im bereich 0..1000 ganzzahlig mit ziemlich vielen 0e. Ein paar Beispielwerte aus dem array:
0 0 0 0 0 0 0 184 931 416 0 0 0 0 0 0
0 0 0 0 0 0 4 648 1000 653 0 0 0 0 0 0Kannst du etwas näheres zum Zusammenhang zur Größe der Werte sagen?
-
asdfasdf schrieb:
Kommt drauf an (das ist das Beste, was ich dazu sagen kann).
-Welchen wert hat N (power of two?)Ja, wenn N eine Zweierpotenz ist, dann sollte es egal sein. Aber dann ist das zweite im Zweifelsfall schneller sein.
-variieren die Werte von data[i] stark oder sind die alle in einem "Bereich"
Das sollte beides keinen Einfluss haben, da man die Bereiche gleichmäßig skaliert. Dann ist auch das zweite im Zweifelsfalls schneller und auch genauer, da weniger Operationen durchgeführt werden und somit weniger Zwischenergebnisse anfallen.
Q schrieb:
Kannst du etwas näheres zum Zusammenhang zur Größe der Werte sagen?
Es ist numerisch ungünstig, Werte von stark unterschiedlicher Größenordnung zu addieren. Besonders, wenn man es sehr oft macht. Denn dann bekommt man (im Vergleich zum kleineren Summand) einen großen relativen Fehler. Beispiel: Du hast 7 Dezimalstellen Rechengenauigkeit und möchtest rechnen 1234567 + 0.1234567. Beide Zahlen sind auf sieben Stellen bekannt, aber das Ergebnis ist 1234567, da das Ergebnis wieder nur auf sieben Stellen genau ist und man hat somit die komplette Information über die 0.1234567 verloren.
Bei deinen beiden Varianten sollte dies keinen Unterschied machen, da es egal ist, ob man die Summanden vorher mit N skaliert oder nicht, die Verhältnisse bleiben schließlich gleich.Man kann sich aber überlegen, wie man an sich diese Art Fehler vermeiden kann (und ob dies überhaupt nötig ist). Ein Weg wurde dir genannt, es gibt noch viele andere, findest du in Numerikbüchern. Meistens ist diese Art von Fehler jedoch eher unwichtig, da gibt es viel Schlimmeres, was man versehentlich machen kann.
-
Kahan schrieb:
Suchst du vielleicht das?
http://en.wikipedia.org/wiki/Kahan_summation_algorithmDanke für den link. Das ist sicherlich die beste variante, mir nur für den Anwendungsfall hier zu aufwändig.
SeppJ schrieb:
Das sollte beides keinen Einfluss haben, da man die Bereiche gleichmäßig skaliert. Dann ist auch das zweite im Zweifelsfalls schneller und auch genauer, da weniger Operationen durchgeführt werden und somit weniger Zwischenergebnisse anfallen.
Also nur am Ende teilen, ok danke.
-
SeppJ schrieb:
Das sollte beides keinen Einfluss haben, da man die Bereiche gleichmäßig skaliert. Dann ist auch das zweite im Zweifelsfalls schneller und auch genauer, da weniger Operationen durchgeführt werden und somit weniger Zwischenergebnisse anfallen.
Ok, Ich gebe zu, dass das Beispiel etwas konstruiert ist, aber hier ist es nicht egal

int main() { float arr[] = {16777218.f, 9.f, 6.f}; float a=0.f, b=0.f; for(unsigned i=0; i!=3; ++i) a+=arr[i]; a/=3.f; for(unsigned i=0; i!=3; ++i) b+=(arr[i]/3.f); //a = 5592411.5 //b = 5592411 (richtig) }
-
Da hatte ich leicht etwas anderes gemeint. Ich dachte du wolltest allgemein auf Fehler durch zu unterschiedlich große Bereiche hinaus. Du demonstrierst hier
SeppJ schrieb:
Dann ist auch das zweite im Zweifelsfalls schneller und auch genauer, da weniger Operationen durchgeführt werden und somit weniger Zwischenergebnisse anfallen.
Die Rundungsfehler entstehen hier, da 3 keine Zweierpotenz ist. Bei einer Zweierpotenz sollte sogar exakt das gleiche herauskommen (außer du konstruierst ein Beispiel, bei dem der Exponent über- bzw. unterläuft.)