mikrocontroller.net

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


Autor: Student (Gast)
Datum:

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

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

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

Autor: Thomas K. (muetze1)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
a/=2*5-8;
=>
a=a/(2*5-8);

Autor: Student (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

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

Edit:
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.
b=b/2*5-8;
Hier kommt die normale Punk-vor-Strich-Regel zur Anwendung: 
b=((b/2)*5)-8.

Autor: casud (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(a/=2*5-8;) != (a=a/(2*5-8);)

Also bei mir kommt das selbe raus.

Autor: Dampfmaschin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann solltest du nochmal zur Grundschule gehen.

Autor: casud (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Justus Skorps (jussa)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Werner B. (werner-b)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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:

int x( int a )
{
  48:   62 e0           ldi     r22, 0x02       ; 2
  4a:   70 e0           ldi     r23, 0x00       ; 0
  4c:   07 d0           rcall   .+14            ; 0x5c <__divmodhi4>
  4e:   cb 01           movw    r24, r22
  a/=2*5-8;
  return a;
}
  50:   08 95           ret

00000052 <x1>:

int x1( int a )
{
  52:   62 e0           ldi     r22, 0x02       ; 2
  54:   70 e0           ldi     r23, 0x00       ; 0
  56:   02 d0           rcall   .+4             ; 0x5c <__divmodhi4>
  58:   cb 01           movw    r24, r22
  a=a/(2*5-8);
  return a;
}
  5a:   08 95           ret


Peter

Autor: casud (Gast)
Datum:

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: casud (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Udo R. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Udo R. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Udo R. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.