Mathematische Berechnungen


  • Mod

    hassanein schrieb:

    Wie aus dem kleinen Programm zu entnehmen ist möchte ich das Ergbnis dieser Aufgabe lösen: k *= i + --k ;

    ist undefiniert.



  • camper schrieb:

    ist undefiniert.

    Halte ich für ein Gerücht.


  • Mod

    johny_legend schrieb:

    camper schrieb:

    ist undefiniert.

    Halte ich für ein Gerücht.

    Wenn das deine Einstellung ist, stellt sich mir die Frage, zu welchem Zweck du dich in diesem Forum äußerst.



  • johny_legend schrieb:

    camper schrieb:

    ist undefiniert.

    Halte ich für ein Gerücht.

    Das kannst du gerne so halten. Kommen wir nun zu den Belegen:

    Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.70)

    ...

    1. This paragraph renders undefined statement expressions such as
      i = ++i + 1;
      a[i++] = i;
      while allowing
      i = i + 1;
      a[i] = i;

    Du bist dran.



  • Quelle?



  • ISO/IEC 9899:1999

    edit: 6.5 2



  • Tim schrieb:

    ISO/IEC 9899:1999

    edit: 6.5 2

    Nun ja, da steht ja schließlich, dass man es so machen _sollte_ und entsprechend ist i+=++i+1 nach dem oberen nicht erlaubt (definiert).
    Was aber nicht heißt, dass das Ergebnis zufällig ist.
    zB. ist das Ergebnis von k *= i + --k ; (Werte wie im Eingangspost) ziemlich immer ziemlich genau 27.



  • johny_legend schrieb:

    Tim schrieb:

    ISO/IEC 9899:1999

    edit: 6.5 2

    Nun ja, da steht ja schließlich, dass man es so machen _sollte_ und entsprechend ist i+=++i+1 nach dem oberen nicht erlaubt (definiert).
    Was aber nicht heißt, dass das Ergebnis zufällig ist.
    zB. ist das Ergebnis von k *= i + --k ; (Werte wie im Eingangspost) ziemlich immer ziemlich genau 27.

    Whoops, es sollte heißen:

    k *= i + k-- ;


  • Mod

    johny_legend schrieb:

    Was aber nicht heißt, dass das Ergebnis zufällig ist.

    stimmt. Da der Standard überhaupt nichts über das Ergebnis aussagt (nicht einmal, was "Ergebnis" in diesem Fall bedeuten soll), legt er sich nicht dahingehend fest, dass dies zufällig sein soll.

    johny_legend schrieb:

    zB. ist das Ergebnis von k *= i + k-- ; (Werte wie im Eingangspost) ziemlich immer ziemlich genau 27.

    das bedarf keines Kommentars.



  • camper schrieb:

    hassanein schrieb:

    Wie aus dem kleinen Programm zu entnehmen ist möchte ich das Ergbnis dieser Aufgabe lösen: k *= i + --k ;

    ist undefiniert.

    War aber genau dein Kommentar.


  • Mod

    johny_legend schrieb:

    camper schrieb:

    hassanein schrieb:

    Wie aus dem kleinen Programm zu entnehmen ist möchte ich das Ergbnis dieser Aufgabe lösen: k *= i + --k ;

    ist undefiniert.

    War aber genau dein Kommentar.

    Da sehe ich keinen Widerspruch. Erklär mir bitte nochmal, wie du bei

    k *= i + k-- ;
    

    mit den Ausgangswerten i=3, k=4 auf das Ergebnis 27 kommst.
    Selbst wenn ich gutwillig bin, sind
    3, 21, 27 und 28
    denkbare Ergebnisse eines realen Compilers.
    Entsprechend den Transformationen

    // (1)
    int temp = k;
    k = k * i + k;
    k = temp - 1;
    
    // (2)
    int temp = k;
    k--;
    k = k * i + temp;
    
    // (3)
    k = k * i + k;
    k--;
    
    // (4)
    int temp = k * i + k;
    k--;
    k = temp;
    


  • Ich habe es wie Nagila Hawa mit Prioritäten begründet:

    k *= i + k-- ;

    (Priorität von Postfix --) <=> k*=i+k;
    k--;

    <=> k = k*(i+k);
    k = k-1;

    (für k=4, i=3) <=> k = 4*(3+4); // = 28
    <=> k = 28 - 1; // =27



  • [quote="johny_legend"]Ich habe es wie Nagila Hawa mit Prioritäten begründet:

    k *= i + k-- ;

    (Priorität von Postfix --) <=>

    k*=i+k;
    k--;
    

    <=>

    k = k*(i+k);
    k = k-1;
    

    (für k=4, i=3) <=>

    k = 4*(3+4); // = 28
     k = 28 - 1;  // =27
    

  • Mod

    johny_legend schrieb:

    Ich habe es wie Nagila Hawa mit Prioritäten begründet:

    Ich sehe hier keine Begründung. Zudem ist die Priorität von Postfix -- höher als die aller anderen Operatoren in dem Ausdruck. Entscheidend ist aber, das Postfix-Dekrement (ebenso wie die Zuweisung) neben einem Wert auch einen Seiteneffekt hat. Wann dieser Seiteneffekt (=die Veränderung des Wertes der Variablen) allerdings stattfindet, ist unspezifiziert (irgendwann zwischen dem vorhergehenden und dem nächsten Sequenzpunkt). Es ist schlicht falsch, zu sagen, dass bei Postfixinkrement zunächst der Wert zurückgegeben wird und danach der Wert des Objektes erniedrigt wird (es ist nur nah genug dran an der Wahrheit, dass man es in einer Einführung für Anfänger stehen lassen kann, um nicht zu sehr zu verwirren). Die Reihenfolge beider Teile einer Operation (Bestimmung des Wertes, Seiteneffekte) bleibt grundsätzlich unspezifiziert (abgesehen von Einschränkungen, die durch die Semantik der Operation impliziert sind).
    In unserem Falle ist der Ausdruck undefiniert, weil er das Objekt k zweimal zwischen den gleichen Sequenzpunkten modifiziert. Zudem findet im Teilausdruck

    k * ( i + k-- );
    

    ein Lesevorgang auf k statt (erster Faktor), der nicht dazu dient, den zu schreibenden Wert für k (bezogen auf das Dekrement) zu bestimmen (offensichtlich findet bei k-- ebenfalls ein Lesevorgang statt, das aber zwingend bevor der neue Wert geschrieben wird = Einschränkung durch Semantik). Der Schreibvorgang durch das Dekrement könnte abgeschlossen sein, bevor der erste Faktor bestimmt ist oder nicht.



  • hassanein schrieb:

    Wie aus dem kleinen Programm zu entnehmen ist möchte ich das Ergbnis dieser Aufgabe lösen: k *= i + --k ;

    das sieht auch für's menschliche auge 'undefiniert' aus.
    willst du sowas:

    k = k*i + k-1;
    k--;
    

    oder das

    k--;
    k = k*i + k;
    

    oder was ganz anderes?
    schreib doch mal hin, wie du's von hand ausrechnen würdest.
    🙂



  • camper schrieb:

    johny_legend schrieb:

    Ich habe es wie Nagila Hawa mit Prioritäten begründet:

    Ich sehe hier keine Begründung. Zudem ist die Priorität von Postfix -- höher als die aller anderen Operatoren in dem Ausdruck. Entscheidend ist aber, das Postfix-Dekrement (ebenso wie die Zuweisung) neben einem Wert auch einen Seiteneffekt hat. Wann dieser Seiteneffekt (=die Veränderung des Wertes der Variablen) allerdings stattfindet, ist unspezifiziert (irgendwann zwischen dem vorhergehenden und dem nächsten Sequenzpunkt). Es ist schlicht falsch, zu sagen, dass bei Postfixinkrement zunächst der Wert zurückgegeben wird und danach der Wert des Objektes erniedrigt wird (es ist nur nah genug dran an der Wahrheit, dass man es in einer Einführung für Anfänger stehen lassen kann, um nicht zu sehr zu verwirren). Die Reihenfolge beider Teile einer Operation (Bestimmung des Wertes, Seiteneffekte) bleibt grundsätzlich unspezifiziert (abgesehen von Einschränkungen, die durch die Semantik der Operation impliziert sind).
    In unserem Falle ist der Ausdruck undefiniert, weil er das Objekt k zweimal zwischen den gleichen Sequenzpunkten modifiziert. Zudem findet im Teilausdruck

    k * ( i + k-- );
    

    ein Lesevorgang auf k statt (erster Faktor), der nicht dazu dient, den zu schreibenden Wert für k (bezogen auf das Dekrement) zu bestimmen (offensichtlich findet bei k-- ebenfalls ein Lesevorgang statt, das aber zwingend bevor der neue Wert geschrieben wird = Einschränkung durch Semantik). Der Schreibvorgang durch das Dekrement könnte abgeschlossen sein, bevor der erste Faktor bestimmt ist oder nicht.

    Dem will ich auch nicht widersprechen. Die Tatsache, dass es laut Standard nicht definiert ist, ist nun wirklich nicht mehr zu diskutieren.
    Ich sage nur, dass es absehbar ist, wie der Compiler den Ausdruck k*=i+k--; aufarbeitet, und zwar nach den Prioritäten:

    Der Postfix-Dekrement-Operator, welcher hier die höchste Priorität hat, sieht laut Standard vor, dass zuerst der aktuelle Wert der Variable zurückgegeben wird, bevor dekrementiert wird:
    (k*=i+k)--

    Nächst kleinere Priorität hat der +-Operator:

    (k*=(i+k))--

    Und dann kommt eben (k=(k*(i+k)))-- raus.

    (ja, es ist syntaktisch falsch; soll aber der Verdeutlichung dienen. Die Klammern sollen die Prioritäten aufzeigen.)

    Du kannst mich aber gerne eines Besseren belehren und mir ein Beispiel zeigen, bei dem dieses Prinzip versagt.



  • johny_legend schrieb:

    Du kannst mich aber gerne eines Besseren belehren und mir ein Beispiel zeigen, bei dem dieses Prinzip versagt.

    Testcode:

    #include <stdio.h>
    
    int func (int k, int i) {
    	k *= i + k--;
    	return k;
    }
    
    int main (void) {
    	printf ("%d\n", func (4,3));
    	return 0;
    }
    

    Testcompiler:

    gcc version 3.4.5 (mingw special)
    

    Test [1]

    D:\temp>gcc ub.c -o ub -O0
    
    D:\temp>ub
    28
    

    Test [2]

    D:\temp>gcc ub.c -o ub -O1
    
    D:\temp>ub
    27
    

  • Mod

    ändert sich was, wenn du

    return k *= i + k--;
    

    schreibst?



  • Dann ist das Ergebnis immer 28 :p

    edit: read "immer" as "mit den Optimierungsstufen O0, O1, O2, O3, Os"



  • camper schrieb:

    ändert sich was, wenn du

    return k *= i + k--;
    

    schreibst?

    Dann müsste immer 28 rauskommen, weil nach return nicht mehr dekrementiert wird.

    Bzw.: Schade. Und ich war mir so sicher. 😞


Anmelden zum Antworten