Forum: Mikrocontroller und Digitale Elektronik Compiler und Konstanten.


von Joachim B. (jojo84)


Lesenswert?

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...

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

>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.

von Alexander (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Joachim B. (jojo84)


Lesenswert?

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ß

von Karl H. (kbuchegg)


Lesenswert?

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."

von Karl H. (kbuchegg)


Lesenswert?

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.

von Joachim B. (jojo84)


Lesenswert?

@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...

von (prx) A. K. (prx)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
1
#define SECONDS_PER_MINUTES  60
2
#define MINUTES_PER_HOUR     60
3
#define SECONDS_PER_HOUR     SECONDS_PER_MINUTES * MINUTES_PER_HOUR
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.

von Andreas F. (aferber)


Lesenswert?

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
int a(void);
2
int b(void);
3
int c(void);
4
5
int x = a()*b()/c();

nicht durch den Standard definiert, in welcher Reihenfolge die drei 
Funktionen aufgerufen werden.

Andreas

von (prx) A. K. (prx)


Lesenswert?

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. ;-)

von Joachim B. (jojo84)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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?

von Grrrr (Gast)


Lesenswert?

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.

von Joachim B. (jojo84)


Lesenswert?

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... =)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Joachim B. (jojo84)


Lesenswert?

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.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Zur Fehlersuche etwas. Schreib dir längere "Formeln" erstmal aus, 
Beispiel:
1
 a = 722+j;
2
 b = 55*i;
3
 c = i%3;
4
 x = pow(a*b/c, f);
Mach das lieber so, bevor du alles in irgendwelche Klammern in ewige 
Ausdrücke formulierst und dir damit a, b und c sparen würdest.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.