Kann ein Conforming-Compiler die Konventionen uint32_t - int16_t - int32_t brechen?

8

Vor kurzem haben wir seltsames Verhalten in altem Code entdeckt. Dieser Code hat seit Ewigkeiten funktioniert, aber auf einigen Plattformen (XBox 360, PowerPC) mit Compileroptimierungen auf max. Normalerweise würde ich ein undefiniertes Verhalten vermuten.

Der Code sieht ungefähr so ​​aus:

%Vor%

Es ist Teil eines Emulators, daher sollte die fragliche Operation nicht zu seltsam sein. Normalerweise würde ich erwarten, dass dies nur die unteren 16 Bits berücksichtigt und diese auf 32 Bits vorzeichen lässt. Offensichtlich war dies das Verhalten, das es seit Ewigkeiten hatte. Auf x86_64 gibt GCC mir dieses Ergebnis:

%Vor%

Nach dem, was ich vom Standard verstehen konnte, ist es jedoch nicht möglich, einen vorzeichenlosen Wert in einen vorzeichenbehafteten umzuwandeln, falls es nicht möglich ist, den Wert des vorzeichenlosen Typs mit dem Typ mit Vorzeichen darzustellen.

Könnte der Compiler dann annehmen, dass der vorzeichenlose Wert im Bereich von [0, 32767] liegen müsste, da jeder andere Wert undefiniert wäre? In diesem Fall würde eine Umwandlung in int16_t und eine weitere Umwandlung in int32_t nichts bewirken. Wäre es für den Compiler in diesem Fall legal, den Code in einen einfachen Schritt zu übersetzen?

    
Maister 09.02.2012, 23:16
quelle

4 Antworten

9

Eine Konvertierung zwischen zwei Integertypen ist niemals undefiniert.

Aber einige Integer-Konvertierungen sind Implementierungen definiert.

Bei ganzzahligen Umwandlungen sagt C:

  

(C99, 6.3.1.3p3) "Ansonsten ist der neue Typ signiert und der Wert kann nicht darin dargestellt werden; entweder ist das Ergebnis implementierungsdefiniert oder es wird ein implementierungsdefiniertes Signal ausgelöst."

Was ist gcc in diesem Fall hier dokumentiert?

Ссылка

  

"Für die Umwandlung in einen Typ der Breite N wird der Wert modulo 2 ^ N reduziert, um innerhalb des Bereichs des Typs zu sein; kein Signal wird ausgelöst"

    
ouah 09.02.2012, 23:27
quelle
2

Wie ouah heißt, führt die Konvertierung eines Bereichs außerhalb des Bereichs zu einem implementierungsdefinierten Ergebnis (oder ermöglicht ein implementierungsdefiniertes Signal angehoben werden).

Zum Beispiel wäre es für eine Implementierung völlig legal zu sagen, dass eine Konvertierung eines Bereichs außerhalb des Bereichs in int16_t nur die unteren 15 Bits des Werts beibehält und immer das Vorzeichenbit auf 0 setzt. Daher würde es Ihre Funktion sign_extend16() einfach als return val & 0x7fff; interpretieren.

Eine Implementierung kann Ihre Funktion jedoch nicht so interpretieren, dass sie val unverändert zurückgibt - die implementierungsdefinierte Konvertierung in int16_t muss zu einem Wert im Bereich% führen co_de%, also muss das Endergebnis irgendwo in int16_t oder [0, 32767] liegen.

Beachten Sie auch, dass die [4294934528, 4294967295] Besetzung dort völlig überflüssig ist.

Zwei Alternativen, die nicht auf implementierungsdefinierten Konvertierungen beruhen, sind (beachten Sie die Änderung des Argumenttyps von int32_t ):

%Vor%

... aber leider scheint der GCC-Optimierer nicht zu bemerken, dass dies nur eine Vorzeichenerweiterung der unteren 16 Bits ist.

    
caf 10.02.2012 03:00
quelle
0

Die zwei Versionen, die ich bereits in den Kommentaren erwähnt habe:

%Vor%

Erzeugt die folgende Ausgabe mit gcc 4.5.3 auf x86-64 mit -O1 :

%Vor%     
Christoph 10.02.2012 07:27
quelle
-1

Verwenden von Union:

%Vor%     
cendar 10.02.2012 05:35
quelle

Tags und Links