Merkwürdigkeit bei /= Operator

Gast #1493117
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...
#1493118
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.
Persönliche Seite #1493156
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.
Gast #1493844
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
#1493896
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
#1494558
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.
Persönliche Seite #1494568
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
#1494580
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.
#1494587
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 :-)
Gast #1494600
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 ;-)
#1494609
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.
Gast #1494612
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.
#1494617
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.
Gast #1494627
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
#1494629
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)
Gast #1494636
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
#1494642
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.
Gast #1494644
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
#1494659
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.
Gast #1494697
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 ;-)
#1494705
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)
#1494821
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren