Klammerung mathematischer Ausdrücke verbindlich für Compiler?
-
Belli schrieb:
Der Compiler darf die Klammern entfernen, weil das Ergebnis mathematisch dasselbe ist. Wenn Du das anders erzwingen willst, wirst Du zwei Schritte machen müssen (und hoffen, daß der Compiler das nicht erkennt wegoptimiert).
Das hätte aber eine böse Konsequenz, besonders bei skaliertem Integerzeugs. Mit der "Optimiererei" hatte ich auch schon viel Spaß.
Bislang hatte ich ja auch noch nie echten Schiffbruch erlitten, aber es sammelt sich so was über die Jahre an Geschreibsel an und die Frage wird immer interessanter, wie "sourcekompatibel" sourcekompatibel wirklich ist.
-
pointercrash() schrieb:
Schlauer wäre es, zuerst (b * c) auszurechnen und dann mit a zu muliplizieren, also
result = (a * (b * c))Jetzt hab' ich einen Compiler hier, der die Klammerung anscheinend einfach entfernt und das dem asm- code zufolge soweit möglich (ansonsten Punkt vor Strich usw. alles korrekt) stur left-to-right abarbeitet.
Kannst du mal Codebeispiele posten? Quellcode und Assemblercode? Das sind Ganzzahlmultiplikationen?
Meine schlichte Frage ist, ob der das theoretisch (also Standard- konform) überhaupt darf

IMHO darf er das nicht, wenn es das Ergebnis beeinflussen kann.
-
pointercrash() schrieb:
Meine schlichte Frage ist, ob der das theoretisch (also Standard- konform) überhaupt darf
wenn ich mich nicht irre, darf er die klammern nie unterschlagen. würde mich jedenfalls wundern, wenn's so wäre (ausser bei int-multiplikationen hintereinander, ohne 'ne andere operation dazwischen).

-
+fricky schrieb:
pointercrash() schrieb:
Meine schlichte Frage ist, ob der das theoretisch (also Standard- konform) überhaupt darf
wenn ich mich nicht irre, darf er die klammern nie unterschlagen. würde mich jedenfalls wundern, wenn's so wäre (ausser bei int-multiplikationen hintereinander, ohne 'ne andere operation dazwischen).

denk nochmal ganz scharf über Bashars IMHO nach.
-
volkard schrieb:
denk nochmal ganz scharf über Bashars IMHO nach.
sorry, ich hab bash0rs posting noch nicht gelesen, als ich meins abschickte.

-
pointercrash() schrieb:
Jetzt hab' ich einen Compiler hier, der die Klammerung anscheinend einfach entfernt und das dem asm- code zufolge soweit möglich (ansonsten Punkt vor Strich usw. alles korrekt) stur left-to-right abarbeitet.
Eine Idee ist es erstmal die Optimierung auszuschalten. Wenn das nicht functioniert dann so etwas - Compiler haben i.m.E. sehr grosse Schwierigkeiten Recursionen zu optimieren:
double MyMult (double rIn_0, double rIn_1) { return (rIn_0 * rIn_1); } MyMult (A, MyMult (B, C));Wuerde ich aber gruendlich kommentieren, warum so gemacht - sieht merkwuerdig aus!
-
Rekursion? Wo?
-
Tim schrieb:
Rekursion? Wo?
Weil Du Du MyMult als Parameter in MyMult aufrufst. Du hast eine Recursion der Tiefe 2.
-
Nein, Rekursion ist was anderes.
-
SG1 schrieb:
Nein, Rekursion ist was anderes.
OK - strikly spoken, aber wenn Du ein "richtige" haben moechtest:
typedef struct MultiData { double rData; MultiData *pNext = NULL; } double MyMulti (MultiData *pIn, double rValue) { if (pIn == NULL) return rValue; else return MyMulti(pIn->pNext, pIn->rValue * rValue); } int main () { MultiData aData [3]; /* Irgentebbes */ aData [0]->rValue = A; aData [1]->rValue = B; aData [2]->rValue = C; aData [0]->pNext = aData [1]; aData [1]->pNext = aData [2]; D = MyMulti (aData, 1); /* Noch ebbes */ }Zufrieden?
-
hartmut1164 schrieb:
SG1 schrieb:
Nein, Rekursion ist was anderes.
OK - strikly spoken, aber wenn Du ein "richtige" haben moechtest:
typedef struct MultiData { double rData; MultiData *pNext = NULL; } double MyMulti (MultiData *pIn, double rValue) { if (pIn == NULL) return rValue; else return MyMulti(pIn->pNext, pIn->rValue * rValue); } int main () { MultiData aData [3]; /* Irgentebbes */ aData [0]->rValue = A; aData [1]->rValue = B; aData [2]->rValue = C; aData [0]->pNext = aData [1]; aData [1]->pNext = aData [2]; D = MyMulti (aData, 1); /* Noch ebbes */ }Zufrieden?
Hey, das kann aber gefährlich werden, denn aData ist nicht mit Null initialisiert, daher wird aData[2].pNext auch nicht Null sein, außerdem sind deine Zuweisungsoperationen illegal, da dein Array keine Zeiger hält.
-
feraL schrieb:
Hey, das kann aber gefährlich werden, denn aData ist nicht mit Null initialisiert, daher wird aData[2].pNext auch nicht Null sein, außerdem sind deine Zuweisungsoperationen illegal, da dein Array keine Zeiger hält.
War etwas schnell mit den Finger - Pointer-Stew gebaut:
#include <stdio.h> struct MultiData { double rValue; struct MultiData *pNext; }; double MyMulti (struct MultiData *pIn, double rValue) { if (pIn == NULL) return rValue; else return MyMulti(pIn->pNext, pIn->rValue * rValue); } int main () { struct MultiData aData [3]; aData[0].rValue = 3.0; aData[1].rValue = 4.0; aData[2].rValue = 5.0; aData [0].pNext = &aData [1]; aData [1].pNext = &aData [2]; aData [2].pNext = NULL; printf ("%e", MyMulti (aData, 1)); return 0; }
-
Also da finde ich sowas wie:
temp = b * c;
result = a * temp;ein klein wenig übersichtlicher, um eine bestimmte Ausführungsreihenfolge zu erzwingen ...
-
Belli schrieb:
Also da finde ich sowas wie:
temp = b * c;
result = a * temp;ein klein wenig übersichtlicher, um eine bestimmte Ausführungsreihenfolge zu erzwingen ...
Hm, in deinem ersten Post in diesem Thread hast du noch geschrieben, bei solchen Konstrukten müsste man hoffen, dass der Compiler das nicht wegoptimiert.
-
Bashar schrieb:
Belli schrieb:
Also da finde ich sowas wie:
temp = b * c;
result = a * temp;ein klein wenig übersichtlicher, um eine bestimmte Ausführungsreihenfolge zu erzwingen ...
Hm, in deinem ersten Post in diesem Thread hast du noch geschrieben, bei solchen Konstrukten müsste man hoffen, dass der Compiler das nicht wegoptimiert.
Jo, stimmt, ich habe keine Ahnung, wie intelligent so ein Compiler ist. Trotzdem würde ich das zunächst so probieren, bevor ich derart wilde Rekursionen entwickeln würde.
-
Warum denn nicht einfach
a * (b * c)? Wenn die Reihenfolge _nicht_ egal ist, und der Compiler es trotzdem umdreht, ist der Compiler kaputt.Wenn a, b, und c z.B. unsigned ints sind, darf der Compiler sie natürlich in der Reihenfolge zusammenmultiplizieren, die ihm am meisten Spaß macht. Und braucht sich auch nicht durch überflüssige, wegoptimierbare Zusatzvariablen und Rekursionen daran hindern lassen.
-
namespace invader schrieb:
Warum denn nicht einfach
a * (b * c)? Wenn die Reihenfolge _nicht_ egal ist, und der Compiler es trotzdem umdreht, ist der Compiler kaputt.Wenn a, b, und c z.B. unsigned ints sind, darf der Compiler sie natürlich in der Reihenfolge zusammenmultiplizieren, die ihm am meisten Spaß macht.
Meine These ist, daß er das unabhängig vom Datentyp darf, einfach weil die Multiplikation kommutativ ist.
Ich habe ehrlich gesagt das Problem 'skalierte Integerarithmetik' nicht verstanden, bin aber unabhängig davon der Meinung, daß der Compiler die Klammern dann nicht zu beachten braucht, wenn das Ergebnis (rein mathematisch betrachtet) davon nicht beeinträchtigt wird.
-
Belli schrieb:
Meine These ist, daß er das unabhängig vom Datentyp darf, einfach weil die Multiplikation kommutativ ist.
Ich habe ehrlich gesagt das Problem 'skalierte Integerarithmetik' nicht verstanden, bin aber unabhängig davon der Meinung, daß der Compiler die Klammern dann nicht zu beachten braucht, wenn das Ergebnis (rein mathematisch betrachtet) davon nicht beeinträchtigt wird.Jaja, Thesen, Vermutungen, ...
1.) Hat sich jemand mal den Standard angesehen oder Assembler Code angeschaut?
2.) Welche Rolle spielt es fuer den Compiler, nicht die Reihenfolge des Nutzer zu beachten? (a*b)*c benoetigt 3 Multiplikationen genau wie a*(b*c).
3.) Rein mathematisch ist schoen und gut, aber wir rechnen im Computer immer mit diskreten Werten. Problematisch wird es wenn sehr grosse reelle Zahlen mit sehr kleinen reellen Zahlen multipliziert/addiert werden. Dort kann das Ergebnis stark von der Reihenfolge der Operationen abhaengen.
-
Der Standard sagt dazu nicht viel explizit, man kann aber das Verhalten ableiten. Ein Ausdruck wie a*(b*c) besteht aus zwei Multiplikationsausdrücken. Der eine ist b*c, der andere besteht aus a und dem Ergebnis von b*c. Folglich würde die abstrakte Maschine zuerst b*c berechnen, dann a damit multiplizieren.
Falls a,b,c komplexe Ausdrücke sind, ist die Auswertungsreihenfolge nicht festgelegt, bei a()*(b()*c()) dürfte also durchaus erst a() aufgerufen werden, dann b()*c() ausgerechnet und zum Schluss multipliziert werden.
Der Compiler darf jetzt jeglichen Code generieren, dessen Semantik mit der der abstrakten Maschine übereinstimmt. Das ist der Freibrief für den Optimierer. (BTW, die Semantik der abstrakten Maschine ist nicht über mathematische Idealisierungen definiert, sondern über Bits und Bytes) Ausnahmen von dieser Regel müssen explizit definiert sein. Sowas gibt es für floating-point-Ausdrücke, bei denen der Compiler gewisse Ausdrücke zusammenfassen darf, wobei eventuell sich ein anderes Rundungsverhalten ergibt. Für normale Ganzzahlausdrücke hab ich sowas nicht gefunden.
-
knivil schrieb:
Belli schrieb:
Meine These ist, daß er das unabhängig vom Datentyp darf, einfach weil die Multiplikation kommutativ ist.
Ich habe ehrlich gesagt das Problem 'skalierte Integerarithmetik' nicht verstanden, bin aber unabhängig davon der Meinung, daß der Compiler die Klammern dann nicht zu beachten braucht, wenn das Ergebnis (rein mathematisch betrachtet) davon nicht beeinträchtigt wird.Jaja, Thesen, Vermutungen, ...
Jaja ... schön, daß Du hier endlich mal mit handfesten Aussagen Licht ins Dunkel bringst ...
knivil schrieb:
1.) Hat sich jemand mal den Standard angesehen oder Assembler Code angeschaut?
Im Eingangsposting steht zu lesen, daß sich der Threadersteller den ASM-Code angesehen hat.
knivil schrieb:
2.) Welche Rolle spielt es fuer den Compiler, nicht die Reihenfolge des Nutzer zu beachten? (a*b)*c benoetigt 3 Multiplikationen genau wie a*(b*c).
Ähhh ..., ist das jetzt neu, oder waren wir nicht schon lange so weit?
knivil schrieb:
3.) Rein mathematisch ist schoen und gut, aber wir rechnen im Computer immer mit diskreten Werten. Problematisch wird es wenn sehr grosse reelle Zahlen mit sehr kleinen reellen Zahlen multipliziert/addiert werden. Dort kann das Ergebnis stark von der Reihenfolge der Operationen abhaengen.
Hier könnte das Problem des Threaderstellers liegen ... das hat er aber selbst schon erkannt, aber super, daß Du das noch mal erwähnst.