2010-08-16 28 views
7

Compte tenu de l'extrait suivant:long long vs multiplication int

#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); 
} 

La sortie avec MinGW GCC 3.4.5 est (-O0):

1 
4 
8 
10000 
-1486618624 

La première multiplication est coulé à l'intérieur d'un int32 (selon la sortie de l'assembleur). La deuxième multiplication n'est pas castée. Je ne suis pas sûr si les résultats diffèrent parce que le programme fonctionnait sur un IA32, ou parce qu'il est défini quelque part dans le standard C. Néanmoins je m'intéresse si ce comportement exact est défini quelque part (ISO/IEC 9899?), Parce que j'aime mieux comprendre pourquoi et quand je dois lancer manuellement (j'ai des problèmes en transférant un programme d'une architecture différente).

Répondre

7

La norme C99 ne précise que les opérateurs binaires tels que * ne fonctionnent pas sur les entiers plus petits que int. Les expressions de ces types sont promues à int avant l'application de l'opérateur. Voir 6.3.1.4 paragraphe 2 et les nombreuses occurrences des mots "promotion entière". Mais ceci est quelque peu orthogonal aux instructions d'assemblage générées par le compilateur, qui fonctionnent sur int s parce que c'est plus rapide même quand le compilateur serait autorisé à calculer un résultat plus court (parce que le résultat est immédiatement stocké dans une l-valeur d'un court type, par exemple).

En ce qui concerne int64 f = d * e;d et e sont de type int, la multiplication se fait comme int selon les mêmes règles de promotion. Le débordement est techniquement undefined comportement, vous obtenez le résultat de deux-s-complément ici, mais vous pourriez obtenir n'importe quoi selon la norme.

Remarque: les règles de promotion distinguent les types signés et non signés lors de la promotion. La règle est de promouvoir les types plus petits à int à moins que int ne puisse représenter toutes les valeurs du type, auquel cas unsigned int est utilisé.

5

Le problème est que la multiplication est int32 * int32, ce qui est fait comme int32, et le résultat est ensuite affecté à un int64. Vous obtiendriez à peu près le même effet avec double d = 3/2;, qui diviserait 3 par 2 en utilisant la division entière, et assignerait 1.0 à d.

Vous devez faire attention au type d'expression ou de sous-expression à chaque fois que cela peut avoir de l'importance. Cela nécessite de s'assurer que l'opération appropriée est calculée comme le type approprié, tel que la conversion d'un des multiplicandes en int64 ou (dans mon exemple) 3.0/2 ou (float)3/2.

+1

Très bien placé. @azraiyl devrait changer cette ligne à 'int64 f = (int64) d * e;' –

+0

Désolé que je n'ai pas déclaré que je connais la solution ici. Ce qui m'intéresse, c'est pourquoi la multiplication est int32 * int32 dans le premier cas et non int8 * int8. Même si la CPU ne supporte que la multiplication int32, elle peut être renvoyée à un int8 après la multiplication. Mais l'instruction IA32 Imul fonctionne pour les registres 8 bits (AL, ...). – azraiyl

+0

@azrayl: Au moins dans C90, C promu tous les opérandes arithmétiques à 'int' s'ils étaient de plus petits types. Un coup d'œil à la norme C99 suggère que ce n'est plus le cas, mais je ne suis pas vraiment sûr. Quel compilateur C utilisez-vous et, le cas échéant, avec quelles options? –

2

a * b est calculée comme un int, et ensuite coulé le type de variable de réception (qui se trouve être int)

d * e est calculée comme un int, puis jeter le type de variable de réception (ce qui arrive juste être int64)

Si l'une des variables de type était plus grande qu'un int (ou un virgule flottante), ce type aurait été utilisé. Mais comme tous les types utilisés dans les multiplications étaient int ou plus petits, ints ont été utilisés.

2

Lire K & R (l'original). Toutes les opérations entières sont effectuées avec le type entier naturel sauf si elles impliquent des variables qui sont (ou sont castées) à quelque chose de plus grand. Les opérations sur char sont castées en 32 bits parce que c'est la taille naturelle de l'entier sur cette architecture. La multiplication des deux entiers de 32 bits se fait en 32 bits car rien ne le fait passer à quelque chose de plus gros (jusqu'à ce que vous l'assigniez à la variable 64 bits, mais c'est trop tard). Si vous souhaitez que l'opération se déroule sur 64 bits, affectez un ou les deux octets à 64 bits.

int64 f = (int64)d * e;