Hallo!
Ich habe grad festgestellt, daß ein Code den ich mir grad betrachte,
noch nicht so richtig zu funktionieren scheint. Ich meine den Fehler auf
die Deklaration der Konstanten beschränken zu können. Aber weil ich mir
da nicht ganz sicher bin hoffe ich auf ein paar klärende Worte von euch
:) ...
Also, Annahme ich habe drei Konstanten:
1
x=12345;
2
y=x*3/1000;
3
// und
4
z=x*y/10;
Jetzt mal Datentypen und sowas außenvor: grundsätzlich möchte ich damit
ja, daß x = 12345, y = 37 und z = 45676 ist.
Nun sind das ja alles Konstanten. x ist ja klar. Bei y aber macht der
Compiler erst das "x * 3" (dann kommt das erwartete raus) oder das "3 /
100"? da würde dann ja für y "0" rauskommen.
Entsprechend bei z: weil y und 10 ja beides zu dem Zeitpunkt bekannte
Konstanten sind könnte der Compiler ja zuerst mal "y / 10" rechnen. wenn
y 37 ist käme dann für z ja 37035 und nicht 45676 raus...
Auch will hier nicht auf Nachkommastellen oder Codestyle raus, mich
interessiert nur brennend, wie der Compiler mit solchen Sachen umgeht,
weil das ja alles Konstanten sind. Aber die Reihenfolge ist ja
wichtig...
>Bei y aber macht der>Compiler erst das "x * 3" (dann kommt das erwartete raus) oder das "3 />100"? da würde dann ja für y "0" rauskommen.
Ganz einfach Mathe ;) Punkt vor Strich und dann von links nach rechts
durch.
Ohne Gewähr:
Grundsätzlich von links nach rechts.
ABER
wenn zwei Operationen gleichwertig sind ( * und / ), dann KANN es sein,
daß der Compiler irgendwas macht. Setze lieber Klammern, v.a. wenn der
Datentyp ansonsten ausgereizt wird.
Alexander schrieb:> wenn zwei Operationen gleichwertig sind ( * und / ), dann KANN es sein,> daß der Compiler irgendwas macht.
Keineswegs. Klammern setzen ist hier zwar vernünftig, aber die
Assoziatitivät ist sehr wohl definiert, hier von links nach rechts.
Sollte auch in jeder operator precedence table drin stehen.
A. K. schrieb:> Alexander schrieb:>>> wenn zwei Operationen gleichwertig sind ( * und / ), dann KANN es sein,>> daß der Compiler irgendwas macht.>> Keineswegs. Klammern setzen ist hier zwar vernünftig, aber die> Assoziatitivät ist sehr wohl definiert, hier von links nach rechts.> Sollte auch in jeder operator precedence table drin stehen.
Genau.
Und da in C (anders als in Mathe) Datentypen sehr wohl eine Rolle
spielen und
y = ( x * 3 ) / 1000;
ein anderes Ergebnis liefert als
y = x * ( 3 / 1000 );
darf der Compiler nicht einfach hergehen und eigenmächtig nach Gutdünken
die Reihenfolge ändern. Für den Compiler gilt die 'As if' Regel. Er darf
alles machen, solange es am Ergbnis nichts ändert und das Ergebnis so
aussieht, as-if der C-Code buchstabengetreu und nach den C-Regeln
abgearbeitet worden wäre. Und diese Regeln sagen nun mal: Operator
Precedence Table und entsprechend der Assoziativität der Operatoren.
Ok, also danke erstmal für die Antworten!
Seid ihr euch denn da sicher? Ich meine Punkt vor Strich ist klar. Aber
bei diesem "von links nach rechts"?
Wenn ich da hätte:
x = y * 10 / 3
Dann wird doch der Compiler sicher die 10 / 3 zuerst rechnen, weil er
das dann ja zu einer 3 zusammenfassen könnte, ODER? Da weiß ich einfach
nicht, wie er sich da benimmt, wenn y schon vorher mal fest definiert
wurde. Gilt das "von links nach rechts" dann immernoch?
Gruß
Joachim A. schrieb:> Ok, also danke erstmal für die Antworten!>> Seid ihr euch denn da sicher?
Ja, da sind wir uns sehr sicher :-)
> Wenn ich da hätte:> x = y * 10 / 3> Dann wird doch der Compiler sicher die 10 / 3 zuerst rechnen,
nein, würde er nicht.
Er kann nicht beweisen, dass in allen Fällen immer das gleiche rauskommt
und damit hat der Optimizer den Riegel vorgeschoben bekommen.
Beitrag "Re: Compiler und Konstanten."
Joachim A. schrieb:
Um auf das ursprüngliche Problem noch einmal zu sprechen zu kommen:
> Ich habe grad festgestellt, daß ein Code den ich mir grad betrachte,> noch nicht so richtig zu funktionieren scheint. Ich meine den Fehler auf> die Deklaration der Konstanten beschränken zu können.
Achte auch darauf, dass du in keinem Schritt einen Über/Unterlauf des
Wertebereichs hast.
In deinem (konstruierten) Beispiel ....
> Also, Annahme ich habe drei Konstanten:>
1
>x=12345;
2
>y=x*3/1000;
3
>// und
4
>z=x*y/10;
5
>
>> Jetzt mal Datentypen und sowas außenvor: grundsätzlich möchte ich damit> ja, daß x = 12345, y = 37 und z = 45676 ist.
.... würde das auf einem AVR mit WinAvr ganz sicher nicht rauskommen,
weil 3 * 12345 -> 37035 ergibt und damit den zulässigen Zahlenbereich
für 16-Bit signed int, -32768 ... +32767, überläuft.
@Karl-Heinz:
jo, danke für den Hinweis. Da hast du natürlich recht. Aber zum Glück
ist das nicht mein Code :) . Darum hab ich in meinem Beispiel ja auch
erstmal Datentypen und sowas weggelassen, weil ich mir weiß, daß man
sich dort gern mal schwer findbare Fehler einhandelt. Und wenn das mein
Code wäre, dann gäbe es da den Divisionsoperator gar nicht ;) ...
Aber was kann ich für mich denn jetzt da als Lehre draus ziehen, unter
was für bedingungen der Optimizer anschlägt? Weil wie gesagt, ICH weiß,
daß das alles Konstanten sind. Wenn der Compiler das auch checkt, dann
check ich nicht, wie man das dann unterscheiden kann...
Joachim A. schrieb:> Aber was kann ich für mich denn jetzt da als Lehre draus ziehen, unter> was für bedingungen der Optimizer anschlägt?
Einfache Regel: Der Optimizer darf genau das, was im Rahmen der
Sprachdefinition nichts am Ergebnis ändert. In diesem Fall ist die
Sprachedefinition klar, daher darf er die Rechenreihenfolge nicht
ändern.
Es gibt aber Fälle, in denen die Sprachdefinition offen ist, nämlich für
"int" und 16 Bits: Da ein Überlauf auftritt und das Überlaufverhalten
bei "int" nicht definiert ist, darf deshalb hier je nach
Optimierungseinstellung ein anderes Ergebnis rauskommen.
Joachim A. schrieb:> Aber was kann ich für mich denn jetzt da als Lehre draus ziehen, unter> was für bedingungen der Optimizer anschlägt?
Wenn du dem Compiler die dezidierte Erlaubnis zum Optimieren geben
willst, dann klammere deine konstanten Ausdrücke. Ansonsten kannst du
davon ausgehen, dass der Compiler eher konservativ sein wird und alles
so übersetzt, wie du es hinschreibst. Bei Integer-Divisionen wird der
Compiler von Haus aus sehr konservativ sein, weil da ja die Kommastellen
sowieso wegfallen und je nachdem wie man einen Ausdruck rechnet
verschiedene Ergebnisse rauskommen.
Bei
wird dir der Optimizer die Multiplikation zusammenziehen, da
1
j=i*SECONDS_PER_HOUR;
immer das gleiche Ergebnis bringt (inklusive möglichen Overflows), egal
wie man es rechnet.
> daß das alles Konstanten sind. Wenn der Compiler das auch checkt, dann> check ich nicht, wie man das dann unterscheiden kann...
Compiler sehen sich den Expression Tree sehr genau an, wenn sie nach
möglichen Optimierungen suchen. Die checken das.
A. K. schrieb:>> wenn zwei Operationen gleichwertig sind ( * und / ), dann KANN es sein,>> daß der Compiler irgendwas macht.> Keineswegs. Klammern setzen ist hier zwar vernünftig, aber die> Assoziatitivät ist sehr wohl definiert, hier von links nach rechts.> Sollte auch in jeder operator precedence table drin stehen.
Ack. Allerdings ist (und daher könnte evtl. das Missverständnis stammen)
bei
1
inta(void);
2
intb(void);
3
intc(void);
4
5
intx=a()*b()/c();
nicht durch den Standard definiert, in welcher Reihenfolge die drei
Funktionen aufgerufen werden.
Andreas
Karl heinz Buchegger schrieb:> .... würde das auf einem AVR mit WinAvr ganz sicher nicht rauskommen,
Undefiniert heisst undefiniert. Es darf daher sogar das "richtige" dabei
herauskommen. ;-)
Oook, nagut, dann nehm ich das mal so hin :) .
Dann würden für mein erstes Beispiel also die Zahlen rauskommen, die ich
erwarte (vorausgesetzt passende Datentypen und so ;) ...), nech?
Joachim A. schrieb:> Oook, nagut, dann nehm ich das mal so hin :) .> Dann würden für mein erstes Beispiel also die Zahlen rauskommen, die ich> erwarte (vorausgesetzt passende Datentypen und so ;) ...), nech?
definiere 'passende Datentypen' und 'welchen Compiler benutzt du auf
welchem System' oder alles auf einen Nenner gebracht: wie gross ist ein
int auf deinem System?
Du müsstest Deinen Blickwinkel hier ein wenig ändern.
Beziehe die Datentypen sowie Operator-Assoziativität und der Rangfolge
mit in deine Überlegungen mit ein. Denn gerade dadurch resultiert das,
was Karl-Heinz beschrieben hat. Deine Überlegung, das der Compiler erst
10 / 3 rechnet, weil da eine schöne 3 herauskommt, widersprechen eben
genau der Assoziativität.
Hm, ich glaub da muß ich mich wirklich noch etwas schlauer machen. Von
diesen Abhängigkeiten hab ich nämlich noch nie gehört... Danke erstmal
für eure Zeit. Weitermachen... =)
Und noch was:
Es ist ziemlich müssig, bei der Fehleranalyse hier auf Fehler im
Optimizer zu bauen. Wenn deine Zahlen nicht stimmen, dann geh davon aus,
dass alles so gerechnet wurde, wie es hingeschrieben ist.
An dieser Stelle ist es enorm höchst unwahrscheinlich, dass du einem
Optimizer-Fehler auf der Spur bist.
Viel wahrscheinlicher sind andere Dinge
* Overflows in Zwischenergebnissen
* Vergessene Klammern, die sich speziell bei Makros fatal
auswirken können
* grundsätzlich falsche Reihenfolge in der Berechnung (Divisionen
die zu früh gemacht werden)
Solche Dinge sind es meistens, die einem das Genick brechen. Das
Optimizer hier daneben greifen, kommt bei etablierten Compilern
praktisch nicht vor.
Ok... klar soweit. Ich persönlich finde Klammern und pingelige
Reihenfolgen auch super. Aber wie gesagt, der Code liegt im Moment so
(ähnlich) vor. Und so bin ich eben dazu gekommen mir über so Dinge wie
den Optimizer und sowas grundsätzliche Gedanken zu machen *.seufz* ...
Ich muß mich an der Stelle mal schlau machen.