2010-08-16 22 views
7

die folgenden Ausschnitt Gegeben:long long vs int Multiplikation

#include <stdio.h> 

typedef signed long long int64; 
typedef signed int int32; 
typedef signed char int8; 

int main() 
{ 
    printf("%i\n", sizeof(int8)); 
    printf("%i\n", sizeof(int32)); 
    printf("%i\n", sizeof(int64)); 

    int8 a = 100; 
    int8 b = 100; 
    int32 c = a * b; 
    printf("%i\n", c); 

    int32 d = 1000000000; 
    int32 e = 1000000000; 
    int64 f = d * e; 
    printf("%I64d\n", f); 
} 

Die Ausgabe mit MinGW GCC 3.4.5 ist (-O0):

1 
4 
8 
10000 
-1486618624 

Die erste Multiplikation mit einem int32 intern gegossen wird (entsprechend der Assemblerausgabe). Die zweite Multiplikation wird nicht gegossen. Ich bin nicht sicher, ob sich die Ergebnisse unterscheiden, weil das Programm auf einem IA32 ausgeführt wurde, oder weil es irgendwo im C-Standard definiert ist. Trotzdem interessiert mich, ob dieses genaue Verhalten irgendwo definiert ist (ISO/IEC 9899?), Weil ich gerne besser verstehe, warum und wann ich manuell casten muss (ich habe Probleme, ein Programm von einer anderen Architektur zu portieren).

Antwort

7

Der C99-Standard spezifiziert, dass binäre Operatoren wie * nicht kleiner als int auf Integer-Typen arbeiten. Ausdrücke dieser Typen werden vor dem Anwenden des Operators auf int heraufgestuft. Siehe 6.3.1.4 Absatz 2 und die zahlreichen Vorkommen der Wörter "ganzzahlige Werbung". Dies ist jedoch etwas orthogonal zu den vom Compiler erzeugten Assemblerbefehlen, die auf ints arbeiten, da dies schneller ist, selbst wenn der Compiler ein kürzeres Ergebnis berechnen könnte (weil das Ergebnis sofort in einem l-Wert eines short gespeichert wird) Typ, zum Beispiel).

In Bezug auf int64 f = d * e; wo d und e sind vom Typ int wird die Multiplikation als int in Übereinstimmung mit den gleichen Beförderungs Regeln. Der Überlauf ist technisch undefined Verhalten, Sie bekommen hier zwei-s-Komplement-Ergebnis, aber Sie könnten alles nach dem Standard bekommen.

Hinweis: In den Ankündigungsregeln werden signierte und unsignierte Typen beim Hochstufen unterschieden. Die Regel besteht darin, kleinere Typen zu int zu fördern, es sei denn, int kann nicht alle Werte des Typs darstellen. In diesem Fall wird unsigned int verwendet.

5

Das Problem besteht darin, dass die Multiplikation int32 * int32 ist, was als int32 erfolgt, und das Ergebnis dann einem int64 zugewiesen wird. Sie würden mit double d = 3/2;, die 3 durch 2 unter Verwendung von Ganzzahldivision zu teilen und den Wert 1.0 zu d zuweisen, den gleichen Effekt erzielen.

Sie müssen auf den Typ eines Ausdrucks oder Unterausdrucks achten, wann immer es darauf ankommt. Dazu muss sichergestellt werden, dass der entsprechende Vorgang als der geeignete Typ berechnet wird, z. B. das Übergeben eines der Multiplikanden an int64 oder (in meinem Beispiel) 3.0/2 oder (float)3/2.

+1

Sehr gut gestellt. @azraiyl sollte diese Zeile in 'int64 f = (int64) d * e ändern;' –

+0

Entschuldigung, dass ich nicht gesagt habe, dass ich die Lösung hier kenne. Was mich interessiert ist, warum Multiplikation im ersten Fall int32 * int32 ist und nicht int8 * int8. Selbst wenn die CPU nur eine Int32-Multiplikation unterstützt, kann sie nach der Multiplikation wieder in ein int8 umgewandelt werden. Der IA32-Imul-Befehl arbeitet jedoch für die 8-Bit-Register (AL, ...). – azraiyl

+0

@azrayl: C förderte zumindest in C90 alle arithmetischen Operanden zu "int", wenn sie von kleineren Typen waren. Ein Blick auf den C99-Standard legt nahe, dass dies nicht mehr der Fall ist, aber ich bin mir nicht wirklich sicher. Welchen C-Compiler benutzen Sie und ggf. welche Optionen? –

2

a * b ist als int berechnet und dann gegossen auf den Empfangsvariablentyp (das int zufällig gerade)

d * e als int berechnet wird, und dann gegossen auf den Empfangsvariablentyp (was passiert einfach int64)

Wenn eine der Typvariablen größer als int (oder Gleitkomma) wäre, dann wäre dieser Typ verwendet worden. Aber da alle Typen, die in den Multiplikationen verwendet wurden, int oder kleiner waren, wurden Ints verwendet.

2

Lesen Sie K & R (das Original). Alle Integer-Operationen werden mit dem natürlichen Integer-Typ ausgeführt, es sei denn, es handelt sich um Variablen, die zu etwas Größerem gehören (oder zu Cassed sind). Die Operationen für char werden auf 32 Bit umgesetzt, da dies die natürliche Größe der ganzen Zahl dieser Architektur ist. Die Multiplikation der zwei 32-Bit-Ganzzahlen erfolgt in 32 Bits, weil nichts zu etwas größerem umgewandelt wird (bis Sie es der 64-Bit-Variablen zuweisen, aber das ist zu spät). Wenn Sie möchten, dass die Operation in 64 Bits erfolgt, setzen Sie einen oder beide Bits auf 64 Bits.

int64 f = (int64)d * e; 
Verwandte Themen