Forum: Compiler & IDEs Merkwürdigkeit bei /= Operator


von Student (Gast)


Lesenswert?

Kann mir jemand erklären warum beim folgenden Beispiel nicht zweimal das 
gleiche ausgegeben wird?

1
int main(void){
2
 int a=0, b=0;  
3
 a/=2*5-8;
4
 b=b/2*5-8;
5
 printf("%d\n", a);
6
 printf("%d\n", b);
7
 return 0;
8
}

Für weiterführende Informationen wenn es kein Fehler ist, wäre ich 
ebenfalls dankbar. Nur nach /= googlet es sich schlecht...

von Thomas K. (muetze1)


Lesenswert?

Punktrechnung geht vor Strichrechnung?

Mit anderen Worten: die verwendeten Unären Operatoren haben eine 
unterschiedliche Priorisierung in der Ausführung. Siehe auch Operator 
Precedence

Bei a wird erst der Term auf der rechten Seite aufgelöst (2) und dann a 
durch die 2 geteilt.

Bei b wird erstmal die Punktrechnung durchgeführt, also b/2*5 bzw. b/10 
und danach die 8 abgezogen und das dann zugewiesen.

von (prx) A. K. (prx)


Lesenswert?

a/=2*5-8;
=>
a=a/(2*5-8);

von Student (Gast)


Lesenswert?

Leider ist aber

(a/=2*5-8;) != (a=a/(2*5-8);)

wers nicht klaubt kann ja selber mal den Schnipsel durch einen Kompiler 
jagen...

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

Der Codeschnipsel oben gibt mir das (erwartete) Ergebnis:
1
0
2
-8
Getestet mit GCC 3.4.4 (cygwin).

Edit:
1
a/=2*5-8;
ist wie prx schon geschrieben hat, gleichbedeutend wie a=a/(2*5-8). Der 
/= Operator nimmt alles was auf der rechten Seite steht als Divisor. 
Deshalb ist das Ergebnis 0.
1
b=b/2*5-8;
Hier kommt die normale Punk-vor-Strich-Regel zur Anwendung: 
b=((b/2)*5)-8.

von casud (Gast)


Lesenswert?

1
(a/=2*5-8;) != (a=a/(2*5-8);)

Also bei mir kommt das selbe raus.

von Dampfmaschin (Gast)


Lesenswert?

Dann solltest du nochmal zur Grundschule gehen.

von casud (Gast)


Lesenswert?

Ok, dann erklär's mir.
Vom Ergebnis ist a/=2 und a=a/2 für mich und für MSVC++9 total das 
selbe.

von Rolf Magnus (Gast)


Lesenswert?

> Vom Ergebnis ist a/=2 und a=a/2 für mich und für MSVC++9 total das
> selbe.

Und was soll das jetzt mit dem Code des Threaderstellers zu tun haben?

von Peter (Gast)


Lesenswert?

> Vom Ergebnis ist a/=2 und a=a/2 für mich und für MSVC++9 total das
> selbe.

also bei mir nicht
C:\>cl test2.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 
for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test2.c
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:test2.exe
test2.obj

C:\>test2.exe
0
-8

von Justus S. (jussa)


Lesenswert?

casud schrieb:
> Ok, dann erklär's mir.
> Vom Ergebnis ist a/=2 und a=a/2 für mich und für MSVC++9 total das
> selbe.

Thomas Pircher hat doch schon geschrieben was der Grund ist...und 
natürlich hat das bei einem simplen /2 keine Auswirkung...

von Werner B. (werner-b)


Lesenswert?

casud schrieb:
> Ok, dann erklär's mir.
> Vom Ergebnis ist a/=2 und a=a/2 für mich und für MSVC++9 total das
> selbe.

Nur in der Fragestellung steht keine "2" sondern "2*5-8"

und a /= 2 wird zu a = 0/2
bzw a = a/2*5-8 wird zu a = ((0/2) * 5) - 8

Siehe http://www.difranco.net/cop2220/op-prec.htm

von Peter D. (peda)


Lesenswert?

Student schrieb:
> Leider ist aber
>
> (a/=2*5-8;) != (a=a/(2*5-8);)
>
> wers nicht klaubt kann ja selber mal den Schnipsel durch einen Kompiler
> jagen...

Nö, der Compiler ist mit mir der Meinung, beides ist identisch:
1
int x( int a )
2
{
3
  48:   62 e0           ldi     r22, 0x02       ; 2
4
  4a:   70 e0           ldi     r23, 0x00       ; 0
5
  4c:   07 d0           rcall   .+14            ; 0x5c <__divmodhi4>
6
  4e:   cb 01           movw    r24, r22
7
  a/=2*5-8;
8
  return a;
9
}
10
  50:   08 95           ret
11
12
00000052 <x1>:
13
14
int x1( int a )
15
{
16
  52:   62 e0           ldi     r22, 0x02       ; 2
17
  54:   70 e0           ldi     r23, 0x00       ; 0
18
  56:   02 d0           rcall   .+4             ; 0x5c <__divmodhi4>
19
  58:   cb 01           movw    r24, r22
20
  a=a/(2*5-8);
21
  return a;
22
}
23
  5a:   08 95           ret


Peter

von casud (Gast)


Lesenswert?

Was bitte ist der Unterschied zwischen a/(2*5-8) und a/2? Jeder schlaue 
Compiler wird die ->Klammer<- auflösen.

von (prx) A. K. (prx)


Lesenswert?

casud schrieb:

> Was bitte ist der Unterschied zwischen a/(2*5-8) und a/2?

Das Ergebnis beispielsweise.

> Jeder schlaue Compiler wird die ->Klammer<- auflösen.

Er wird erst den Inhalt der Klammer rechnen und dann dividieren. Bei 
a/2*5-8 wird er erst dividieren und dann weiterrechnen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Der Thread ist köstlich. Falls es noch keinem aufgefallen ist: Hier 
schwirren gerade drei fast identische Codeteile herum. Und von jedem 
wird was anderes behauptet.

Die Verwirrung ist echt komplett!

Es sollte aber, denke ich, klar geworden sein, dass
 a/=2*5-8;
nicht das Gleiche ist, wie
 b=b/2*5-8;

Das liegt aber nicht am Compiler, sondern an den vorherrschenden 
Mathematikregeln :P

von (prx) A. K. (prx)


Lesenswert?

Simon K. schrieb:

> Das liegt aber nicht am Compiler, sondern an den vorherrschenden
> Mathematikregeln :P

Zwischen denen ursächlicher Zusammenhang besteht ;-). Es gibt aber 
Sprachen die von die Mathematikregeln gänzlich anderere Ansichten haben, 
bei denen beispielsweise bei a/(2*5-8) und bei a/2*5-8 stets exakt das 
gleiche Ergebnis rauskommt.

von Klaus W. (mfgkw)


Lesenswert?

Simon K. schrieb:
> Der Thread ist köstlich. Falls es noch keinem aufgefallen ist: Hier
> schwirren gerade drei fast identische Codeteile herum. Und von jedem
> wird was anderes behauptet.
>
> Die Verwirrung ist echt komplett!
>
> Es sollte aber, denke ich, klar geworden sein, dass
>  a/=2*5-8;
> nicht das Gleiche ist, wie
>  b=b/2*5-8;
>
> Das liegt aber nicht am Compiler, sondern an den vorherrschenden
> Mathematikregeln :P

und daran, daß a etwas anderes ist als b :-)

von yalu (Gast)


Lesenswert?

A. K. schrieb:
> Es gibt aber Sprachen die von die Mathematikregeln gänzlich anderere
> Ansichten haben, bei denen beispielsweise bei a/(2*5-8) und bei
> a/2*5-8 stets exakt das gleiche Ergebnis rauskommt.

Igittigitt! Was sind denn das für welche? Eine davon heißt ganz sicher
Obfusca ;-)

von (prx) A. K. (prx)


Lesenswert?

yalu schrieb:

> Igittigitt! Was sind denn das für welche? Eine davon heißt ganz sicher
> Obfusca ;-)

Ich habe dabei mal grosszügig / als Division und * als Multiplikation 
verstanden, wenngleich das zumindest in der Originalversion der Sprache 
etwas anders aussieht. Aber es gibt auch Abirrungen die mit ASCII 
arbeiten.

Und die Logik der Rechenweise von APL ist doch wirklich ganz einfach und 
übersichtlich: unter Missachtung aller althergebrachter Vorrangregeln 
strikt von rechts nach links.

Aber klassische APL-Programme haben schon was Obfusca-mässiges. Ein paar 
Zeilen Sonderzeichengewirr, das aussieht wie ein Binärfile im 
Texteditor, an Stelle von etlichen Seiten C.

von casud (Gast)


Lesenswert?

>> Was bitte ist der Unterschied zwischen a/(2*5-8) und a/2?
>Das Ergebnis beispielsweise.

Ahja. Also kommt bei a/(2*5-8) und a/2 etwas unterschiedliches raus 
interessant. Halt ich zwar immernoch für ein Gerücht aber wer's mag.

Ich bezog mich nie auf "a/2*5-8" sondern auf "a/(2*5-8)" und jemand in 
diesem Thread meint das: a=a/(2*5-8) != a /= 2*5-8. Aber ich bin mir 
hundertprozentig sicher das der Compiler hier sogar den selben Code 
generieren wird. Siehe Auszug von peda.

von Karl H. (kbuchegg)


Lesenswert?

casud schrieb:
>>> Was bitte ist der Unterschied zwischen a/(2*5-8) und a/2?
>>Das Ergebnis beispielsweise.
>
> Ahja. Also kommt bei a/(2*5-8) und a/2 etwas unterschiedliches raus
> interessant. Halt ich zwar immernoch für ein Gerücht aber wer's mag.

Hier betreibst du jetzt Haarspalterei.
Es geht nicht darum, dass 2*5-8 zufällig auch 2 ergibt. Und wenns den 
anderen so ergeht wie mir, dann haben sie das auch bis vor 20 Sekunden 
noch nicht gemerkt.

Es geht darum, dass

  a/2*5-8 und  a/(2*5-8)

unterschiedliche Auswertungen liefern.

Mit demselben recht könnte ich ja auch sagen, dass
   x * x == x + x

denn für

   2 * 2 == 2 + 2

stimmts ja.

> Siehe Auszug von peda.
Der beweist aber eigentlich damit etwas völlig anderes.


Jetzt betreibe ich auch mal Haarspalterei
> Was bitte ist der Unterschied zwischen a/(2*5-8) und a/2?
> Jeder schlaue Compiler wird die ->Klammer<- auflösen.

Jeder schlaue Compiler wird die Klammer eben nicht auflösen, sondern er 
wird ein Constant Folding betreiben und den Ausdruck in der Klammer zur 
Compilezeit evaluieren (=berechnen)

Unter Klammer auflösen versteht man etwas anderes :-)

  a*( 3 + 5 )   ==>   a*3 + a*5

und genau das wird der Compiler sicher nicht tun.

von (prx) A. K. (prx)


Lesenswert?

Simon K. schrieb:

> Die Verwirrung ist echt komplett!

Das ist das schöne an solchen Threads. Hat was von Monty Python. Muss ja 
nicht immer bierernst sein.

von Udo R. S. (Gast)


Lesenswert?

Leute bleibt doch mal auf dem Boden.

Zum ersten: Punkt geht vor Strichrechnung:
Das bedeutet daß a/(b-c) etwas anderes ist als a/b-c.

Das zweite hier viel wichtigere ist die Priorität der Operatoren der 
Programmiersprache. Das hängt von der Programmiersprache ab und ggf. hat 
da ein Compiler auch mal ein Fehler.
der Operator y /=x bedeutet y = y/x
Die Frage ist wie setzt der Compiler das um wenn x = b-c ist:
entweder y = y/(b-c) oder y = y/b-c
Das hängt von der Priorität der Operatoren '/=' und '-' ab und sollte in 
der Sprach und/oder Compilerdoku zu finden sein.
Logisch wäre hier die Priorisierung '/=' ganz zuletzt also y = y/(b-c)

Gruß, Udo

von Karl H. (kbuchegg)


Lesenswert?

Udo R. S. schrieb:

> Programmiersprache. Das hängt von der Programmiersprache ab und ggf. hat
> da ein Compiler auch mal ein Fehler.

Möglich wenn auch unwahrscheinlich.

> der Operator y /=x bedeutet y = y/x

definiere das doch gleich richtig

    y /= x   <==>  y = y / (x)

und damit erübrigt sich dann auch die Betrachtung der Operator 
Precendence, die sowieso nicht zutrifft, denn im Resultat deiner 
Umformung kommt /= gar nicht mehr vor.

Die Operator Precedence regelt Dinge wie das hier

    a = y /= 5 * 2;

welches als

    a = ( y /= 5 * 2 );

angesehen werden muss und nicht als

    a = ( y /= 5 ) * 2;

(Nicht das ich jemandem explizit empfehlen würde sowas zu schreiben. 
Aber der C-Standard muss so etwas regeln)

von Udo R. S. (Gast)


Lesenswert?

Noch was
@Simon K: a ist zwar nicht immer b aber in dem Beispiel war a = b und 
nur deshalb in zwei Variablen gepackt um es für den Verfasser 
übersichtlicher zu gestalten.

Aber wichtiger: Ihr rechnet hier mit Integer Variablen. Das heisst es 
wird Ganzzahlarithmetik ohne Rundung betrieben.
8/5 ist da 1 (zumindest in C) und 8*2/5 was anderes als 8/5*2
nämlich:
8*2 = 16   16/5 = 3 (rest 1 fliegt einfach weg)
8/5 = 1 (Rest 3 fliegt weg) 1*2 = 2

Mein Tip programmiert Microcontroller immer in Assembler, da wisst ihr 
wenigstens was passiert :-)

Gruß, Udo

von Karl H. (kbuchegg)


Lesenswert?

Udo R. S. schrieb:

> Mein Tip programmiert Microcontroller immer in Assembler, da wisst ihr
> wenigstens was passiert :-)

Oder lernt die C-Regeln.
So schwer sind die nicht und der Komfort dass sich der Compiler um das 
Auswerten von komplexeren Ausdrücken kümmert ist nicht von der Hand zu 
weisen.

von Udo R. S. (Gast)


Lesenswert?

@Karl Heinz Buchegger
Da könnte man jetzt drüber streiten :-)
Du definierst in Deinem Posting implizit daß '/=' eine niedrigere 
Operator Precendence hat als alle Operatoren, die danach benutzt werden.
Ich habe gerade mal nachgesehen, bei Java ist das auch so und ich nehme 
stark an daß das in C auch so ist.
ich wollte hier nur darauf hinweisen, daß es so etwas wie eine Priorität 
der Operatoren gibt und daß man da ein Auge darauf haben sollte wenn man 
in Hochsprachen programmiert.

Und dass an solchen Ecken alle Compiler feherfrei sind würde ich nicht 
ungesehen unterschreiben.

Gruß, Udo

von P. S. (Gast)


Lesenswert?

Man kann auch einfach Klammern. Das ist natuerlich nicht elitaer, aber 
ich habe einfach schon zu oft Code gesehen, bei dem der Autor meinte 
er wuesste jede Praezedenzregel auswendig.

von Karl H. (kbuchegg)


Lesenswert?

Udo R. S. schrieb:
> Du definierst in Deinem Posting implizit daß '/=' eine niedrigere
> Operator Precendence hat als alle Operatoren, die danach benutzt werden.

Nicht ich definiere das, sondern der C-Standard.
Und die Prioritäten sind so gewählt, dass sich automatisch die Punkt vor 
Strich Rechnung ergibt. Alle anderen Operatoren sind in diese Tabelle 
auch eingefügt, wobei es bei manchen eine Überraschung gibt.

> ich wollte hier nur darauf hinweisen, daß es so etwas wie eine Priorität
> der Operatoren gibt und daß man da ein Auge darauf haben sollte wenn man
> in Hochsprachen programmiert.

Das unterchreibe ich vorbehaltlos.

> Und dass an solchen Ecken alle Compiler feherfrei sind würde ich nicht
> ungesehen unterschreiben.

Och. Du unterschätzt die Compilerbauer.
Die Tabelle der Precedence ist vorgegeben und bevor ein Compiler auf den 
Markt geschmissen wird, durchläuft er erst mal eine Testsuite die ihn 
auf ein paar hundert Testprogramme loslässt die unter anderem auch das 
korrekte Auswerten von Ausdrücken umfassen.
Wenn es hier einen Fehler gibt, fällt das sehr schnell auf noch ehe der 
Compiler auf den Markt kommt.

(Ein beliebter Test ist zb. dass sich ein Compiler selbst übersetzen 
muss. Mit dem erhaltenen EXE wird dann der Compiler noch einmal selbst 
übersetzt.
Die Ergebnisse aus dem Zwischendruchgang und dem Endergebnis müssen Byte 
für Byte übereinstimmen(*). Und da ein Compiler ein nichttriviales 
Programm darstellt hat man dadurch schon einen guten Teil der 
Funktionalitäten getestet. Das ist aber eher sowas wie ein Quick&Dirty 
Rundumschlagtest. Das Durchlaufen der Testsuiten erfolgt natürlich immer 
noch)

(*) Da habe ich jetzt eine Stufe vergessen. Der ganze Prozess ist 4 
stufig. Der A Compiler erzeugt den Compiler B.
Wenn sich B den Source vornimmt und Compiler C erzeugt UND C aus 
demselben Source einen Compiler D erzeugt, dann müssen C und D Byte für 
Byte identisch sein.
Die Compilerversion B ist ja mit einem 'alten Compiler A' entstanden und 
kann daher abweichen.

von yalu (Gast)


Lesenswert?

A. K. schrieb:
> Und die Logik der Rechenweise von APL ist doch wirklich ganz einfach und
> übersichtlich: unter Missachtung aller althergebrachter Vorrangregeln
> strikt von rechts nach links.

Stimmt, das ist ja ganz einfach. Ich hatte etwas in diese Richtung
geahnt, hatte aber erst nicht berücksichtigt, dass die rechtsassoziative
Auswertung natürlich auch innerhalb des Klammerausdrucks in a÷(2×5−8)
Anwendung findet. Beachtet man dies, liefert der Ausdruck tatsächlich
das gleiche Ergebnis wie a÷2×5−8, nämlich a÷¯6.

Ich glaube, APL ist die Superangebersprache, deswegen muss ich sie mir
irgendwann mal reinziehen. Die lustigen Symbole, die ungewohnte Auswer-
tungsreihenfolge, die nahezu redundanzfreie Syntax ... da stimmt einfach
alles ;-)

von Karl H. (kbuchegg)


Lesenswert?

yalu schrieb:
> A. K. schrieb:
>> Und die Logik der Rechenweise von APL ist doch wirklich ganz einfach und
>> übersichtlich: unter Missachtung aller althergebrachter Vorrangregeln
>> strikt von rechts nach links.
>
> Stimmt, das ist ja ganz einfach. Ich hatte etwas in diese Richtung
> geahnt, hatte aber erst nicht berücksichtigt, dass die rechtsassoziative
> Auswertung natürlich auch innerhalb des Klammerausdrucks in a÷(2×5−8)
> Anwendung findet. Beachtet man dies, liefert der Ausdruck tatsächlich
> das gleiche Ergebnis wie a÷2×5−8, nämlich a÷¯6.
>
> Ich glaube, APL ist die Superangebersprache, deswegen muss ich sie mir
> irgendwann mal reinziehen. Die lustigen Symbole, die ungewohnte Auswer-
> tungsreihenfolge, die nahezu redundanzfreie Syntax ... da stimmt einfach
> alles ;-)

Nicht zu vergessen:
Das komplette Referenzmanual passt auf eine beidseitig bedruckte Karte 
von der Größe einer Scheckkarte :-)
(Wenn ich meine noch mal finde, scanne ich sie)

von (prx) A. K. (prx)


Lesenswert?

yalu schrieb:

> Ich glaube, APL ist die Superangebersprache,

Ja, in solchem Kontext wie hier ist das tatsächlich ein bischen so als 
ob jemand sagt, er könne koreanisch lesen. Allerdings hatte ich mir die 
nicht ausgesucht. Das war schlicht die einzige Sprache, die damals in 
der Schule zur Verfügung stand.

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.