2015-11-16 16 views
5

Als Follow-up zu „https://stackoverflow.com/questions/33732041/why-static-castunsigned-intushrt-maxushrt-max-yields-correct-valueFührt die Multiplikation von unsigned short zu undefiniertem Verhalten?

ich mich frage, ob alle Arten der Förderung (mit Ausnahme von einigen Ausnahmen) mit einem niedrigen Rang als int zu int arithmetischen Operationen durchzuführen, in einigen Fällen können dazu führen, UB.

zB:

unsigned short a = 0xFFFF; 
unsigned short b = a*a; 

Als unsigned short gefördert wird int für Operationen Arithmetik dies zur Folge hätte:

unsigned short a = 0xFFFF; 
unsigned short b = (int)a*(int)a; 

Als (int)0xFFFF*(int)0xFFFF verursacht einen Überlauf und Überlauf von signierten Typen UB: Kann zwei vorzeichenlose Kurzschlüsse multiplizieren x,y verursachen undefiniertes Verhalten in dem Fall x*y > INT_MAX


UPDATE:

Die Frage zielt speziell auf den Fall, dass 32-Bit-int und short ist 16 Bit.

+0

Ja. Das Ergebnis ist, keine Arithmetik mit vorzeichenlosen Typen eines niedrigeren Umwandlungsrangs als "int" auszuführen. Eine einfachere Regel besteht darin, keine vorzeichenbehafteten Typen für Zahlen zu verwenden, sondern sie für Bit-Fiddling zu verwenden. –

+0

Ja, dies ist ein vorzeichenbehafteter ganzzahliger Überlauf, der UB verursacht. Eine lästige historische Warze, und sie kann in Verkleidung auftreten, weil 'uint16_t' oft als typedef für' unsigned short' implementiert wird. Theoretisch könnte das gleiche Problem sogar mit "uint32_t" auftreten, da es nichts gibt, was einen Compiler stoppt, der zum Beispiel "kurz" 32-Bit auf einem System mit 64-Bit "int" macht. –

+0

@ M.M: Eigentlich ist hier nichts besonderes an "short". Jeder vorzeichenlose Typ, der kleiner als "int" ist, wird vor dem Ausführen von arithmetischen Operationen zu "int" hochgestuft. Dies gilt insbesondere für "uint32_t", wenn "int" 64-Bit ist. –

Antwort

8
C++ 11 §3.9.1/4, vollständige Zitat:

Unsigned Integer, erklärt unsigned, müssen die Gesetze der Arithmetik Modulo gehorchen 2 n wo n das ist Nummer von Bits in der Wertdarstellung dieser bestimmten Größe der ganzen Zahl.

Abgesehen von der etwas irreführenden Formulierung über “ erklärt unsigned ” könnte dies gelten scheint, dass jeder arithmetischer Ausdruck, der nur Argument von einem gegebenen Typ ohne Vorzeichen beinhalten, um ein Ergebnis Modulo ergeben wird 2 n für diese Art .

Jedoch gibt es keine arithmetische Ausdrücke auf allen für unsigned Typen von niedrigeren Umwandlungs Rang als int: alle Argumente in einem scheinbaren solchen Expressions konvertiert bis (1) mindestens int, oder in Abhängigkeit von der Anzahl Bereiche von die C++ Implementierung, bis zu unsigned int.

Als Ergebnis a*b wo a und b sind unsigned short Werte, (2) kann formal nicht definiertes Verhalten haben. Weil es kein unsigned short Ausdruck ist. Es ist (in der Praxis) ein int Ausdruck.

Das heißt, mit einem vernünftigen Compiler, der keine speziellen Gehäuse einführt, wo es formal UB, und mit in-Praxis 8-Bit-Bytes und unsigned short max-Wert, der durch int ist darstellbare bemerkt, und gemeinsame Komplement vorzeichenbehaftete Ganzzahl-Darstellung der beiden, das Ergebnis wird, wenn es zurück in unsigned short konvertiert wird, sein, als ob es modulare Arithmetik im Bereich von unsigned short wäre. Das ist, weil Zweierkomplement, an der Maschinencode-Ebene, nur modulare Arithmetik mit einem Bereich zentrierte auf 0.


(1) In der Praxis wird man in der Regel einen 8 Bit pro Byte verwenden Implementierung, wo der Maximalwert von unsigned short passt gut in die int Bereich, so in der Praxis sprechen wir über eine Umwandlung bis int.
(2) Eg, für 16-Bit unsigned short und 32-Bit-int, (2 -1) = 2 -2 × 2 +1> 2 -1, wobei der letzte Wert der maximale positive Wert int ist.

+0

Irgendeine Idee, warum vorzeichenlose Typen nicht nur auf 'unsigned int' befördert werden? Dies würde das Problem lösen. –

+0

@SimonKraemer: Ich weiß es nicht. Aber ich erinnere mich, dass James Kanze einmal bemerkte, dass die Umstellungsregeln schon früh geändert wurden. Das war im Zusammenhang mit den Problemen des Mischens von Vorzeichen und Vorzeichen in Ausdrücken. –

+0

Nun, es sollte im wirklichen Leben keine Rolle spielen. Danke für das Ausgraben der Regeln. –

3

Wenn Sie unsigned short * unsigned short multiplizieren, gibt es eine implicit conversion und der Wert wird in C++ 11 an int übergeben. Die documentation sagt:

Prvalues ​​von kleinen integralen Typen (wie char) auf prvalues ​​größeren integralen Typen (wie int) umgewandelt werden. Insbesondere arithmetische Operatoren akzeptieren keine Typen kleiner als int als Argument

So wird es in einem undefinierten Verhalten führen.