Sicher, in C ist die Reihenfolge der Operandenauswertung undefiniert
Das stimmt nicht. C verwendet eine left-most, inner-most Auswertungsstrategie für arithmetische Operatoren.
Wir reden hier von 3 verschiedenen Dingen. Du sprichst von left-most, inner-most, was sich auf die Auswertung von Funktionsargumenten bezieht, aber nicht auf die Auswertung von Ausdrücken als solches. Dazu kommt, dass deiner Behauptung Aussage vom C99 Standard widersprochen wird: Annex J sagt, dass die Reihenfolge der Auswertung von Funktionsargumenten undefiniert ist. Für C11 habe ich kein offizielles Standarddokument gefunden.
Zum zweiten ist da die Reihenfolge, wie die Werte von Operanden für Operatoren bestimmt werden. Die ist per Definitionem undefiniert, und das begründen K&R damit, dass sie dem Programmierer abgewöhnen wollen, sich auf Compilerspezifika zu verlassen. In C99 steht diese Undefiniertheit - wieder Annex J - auch noch drin. D.h. wenn die Operanden A,B,C in einem Term x=A+B+C
Funktionsaufrufe oder Teilausdrücke sind, dann ist die Reihenfolge der Funktionsaufrufe bzw. die Werteermittlung undefiniert.
Drittens haben wir die Reihenfolge, in der Operatoren auf die gefundenen Werte angewendet werden. Und die ist NICHT undefiniert, die ist über die Assoziativität klar festgelegt. D.h. wenn ich A+B+C schreibe, wird erst A+B gerechnet und dazu C addiert. Im C99 Standard steht, dass der Compiler das bei Integer-Operationen umordnen darf, wenn die Hardware es zulässt (sprich: keine Overflow-Interrupts entstehen und Überläufe sich gegenseitig neutralisieren). Aber eben nur bei Integer, nicht bei Float. Gerade weil wegen der begrenzten Genauigkeit von float/double Variablen das Kommutativ-, Assozativ- und Distributivgesetz der Mathematik nicht anwendbar sind, darf der Compiler nicht umordnen, sondern muss sich strikt an die definierte Anordnung halten.
Das stimmt nun wieder, aber das bedeutet nicht, dass die Addition auch dem Assoziativitätsgesetz folgt. Wir benutzen hier „Assoziativität“ für zwei unterschiedliche Sachverhalte, das sollten wir nicht durcheinander werfen.
Genau :)
Deswegen sind die Summanden x2 und x4 nach der Zuweisung an die Akkumulatorvariable float y
verschwunden.
Das stimmt ebenfalls, das ist der Auslöschungseffekt. Es erklärt aber nicht, wieso der Fehler in der einen Berechnung auftritt und in der anderen nicht. Jedenfalls nicht, wenn man davon ausgeht, dass das Ergebnis einer Floating-Point-Addition wieder eine Floating-Point-Zahl ist.
Nein, davon wird es nicht erklärt. Das kommt später.
Meine Vermutung war deshalb, dass mit einer Optimierungsstufe kompiliert wurde, die es dem Compiler erlaubt die Reihenfolge der Additions-Anwendungen zu variieren. Dann könnte aus a + b + c
nämlich c + b + a
geworden sein. Dann könnte in der einen Berechnung Auslöschung auftreten und in der anderen nicht. Deshalb kam ich auf die Nicht-Assoziativität.
Du meinst (a+b)+c und a+(b+c), andernfalls wäre es Kommutativität ;-). Das Thema "Umordnen von Float-Ausdrücken" hatte ich oben schon erwähnt, und ich habe nichts vermutet, sondern im Standard nachgelesen.
Wirft man meine Annahme über Board, könnte man den Effekt auch damit erklären, dass C intern mit höherer Präzision rechnet. Darauf wolltest du vermutlich hinaus. Im Moment erscheint mir beides gleich wahrscheinlich.
Wir brauchen keine Wahrscheinlichkeitsrechnung, es ist im Standard definiert (5.1.2.3 Nr. 11; 5.2.4.2.1, Nr. 8)): Float-Berechnungen werden mit höchstmöglicher Präzision ausgeführt und erst beim Zuweisen wird gerundet. Der Compiler DARF mit kleinerer Genauigkeit rechnen, wenn er für sich beweisen kann, dass das zum gleichen Ergebnis führt wie die genauere Rechnung.
Rolf