2010-08-16 5 views
7

Учитывая следующий фрагмент:долго долго против ИНТ умножения

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

Выход с MinGW GCC 3.4.5 является (-O0):

1 
4 
8 
10000 
-1486618624 

Первое умножение отливают на int32 внутренне (в соответствии с выходом ассемблера). Второе умножение не выполняется. Я не уверен, отличаются ли результаты, потому что программа была запущена на IA32, или потому, что она определена где-то в стандарте C. Тем не менее мне интересно, если это точное поведение определено где-то (ISO/IEC 9899?), Потому что мне нравится лучше понимать, почему и когда я должен вручную накладывать (у меня проблемы с переносом программы из другой архитектуры).

ответ

7

Стандарт C99 действительно указывается, что бинарные операторы, такие как * не работают на целых типов меньше int. Выражения этих типов повышаются до int до того, как оператор будет применен. См. Пункт 6.3.1.4 параграфа 2 и многочисленные вхождения слов «цельное продвижение». Но это несколько ортогонально инструкциям сборки, сгенерированным компилятором, которые работают с int, потому что это происходит быстрее, даже когда компилятору разрешено вычислять более короткий результат (потому что результат немедленно сохраняется в l-значении короткого тип, например).

Что касается int64 f = d * e; где d и e имеют тип int, умножение выполняется как int в соответствии с теми же правилами продвижения по службе. Переполнение технически неопределенное поведение, вы получаете результат с двумя сложениями, но вы можете получить что-либо в соответствии со стандартом.

Примечание: правила поощрения различают подписанные и неподписанные типы при продвижении. Правило состоит в том, чтобы продвигать меньшие типы до int, если int не может представлять все значения типа, и в этом случае используется unsigned int.

5

Проблема заключается в том, что умножение - int32 * int32, которое выполняется как int32, а результат затем присваивается int64. Вы получите такой же эффект с double d = 3/2;, который разделил бы 3 на 2, используя целочисленное деление, и назначил 1.0 d.

Вы должны обратить внимание на тип выражения или подвыражения, если это имеет значение. Для этого требуется удостовериться, что соответствующая операция рассчитана как соответствующий тип, такой как литье одного из множителей в int64 или (в моем примере) 3.0/2 или .

+1

Очень хорошо поставил. @azraiyl должен изменить эту строку на 'int64 f = (int64) d * e;' –

+0

Извините, что я не сказал, что знаю это решение здесь. Меня интересует, почему умножение int32 * int32 в первом случае, а не int8 * int8. Даже если процессор поддерживает только умножение int32, он может быть возвращен в int8 после умножения. Но инструкция IA32 imul работает для 8-битных регистров (AL, ...). – azraiyl

+0

@azrayl: По крайней мере, на C90, C продвигал все арифметические операнды до 'int', если они были меньшего размера. Взгляд на стандарт C99 предполагает, что это уже не так, но я не уверен. Какой компилятор C вы используете, и, если применимо, с какими параметрами? –

2

a * b рассчитывается как Int, а затем приведения к принимающему переменному типу (который как раз случается быть целое)

d * e рассчитываются как Int, а затем приведения к принимающему переменному типу (который как раз случается to int64)

Если какая-либо из переменных типа была больше, чем int (или была плавающей точкой), то этот тип использовался бы. Но так как все типы, используемые в умножениях, были int или меньше, использовались ints.

2

Прочитано K & R (оригинал). Все целые операции выполняются с помощью натурального целочисленного типа, если только он не включает в себя переменные, которые (или отбрасываются) к чему-то большему. Операции с char передаются на 32 бита, потому что это натуральный размер целых чисел в этой архитектуре. Умножение двух 32-битных целых чисел выполняется в 32 битах, потому что ничто не бросает его на что-либо большее (пока вы не назначите его 64-битной переменной, но это слишком поздно). Если вы хотите, чтобы операция выполнялась в 64 битах, отбросьте один или оба из них до 64 бит.

int64 f = (int64)d * e; 
Смежные вопросы