2015-01-13 13 views
7

Мне нужно взять 2 неподписанных 8-битных значения и вычесть их, а затем добавить это значение в 32-разрядный аккумулятор. 8-битное вычитание может зайти, и это нормально (unsigned int underflow определяется поведением, поэтому проблем нет).Почему static_cast на выражении действует дистрибутивно?

Я бы ожидать, что static_cast<uint32_t>(foo - bar) должен делать то, что я хочу (где foo и bar являются как uint8_t). Но похоже, что это отбрасывает их сначала, а , тогда выполняет 32-битное вычитание, тогда как мне нужно, чтобы оно было под потоком как 8-битная переменная. Я знаю, что смогу всего лишь 256, но я пытаюсь найти , почему работает так.

Пример здесь: https://ideone.com/TwOmTO

uint8_t foo = 5; 
uint8_t bar = 250; 

uint8_t diff8bit = foo - bar; 
uint32_t diff1 = static_cast<uint32_t>(diff8bit); 

uint32_t diff2 = static_cast<uint32_t>(foo) - static_cast<uint32_t>(bar); 

uint32_t diff3 = static_cast<uint32_t>(foo - bar); 

printf("diff1 = %u\n", diff1); 
printf("diff2 = %u\n", diff2); 
printf("diff3 = %u\n", diff3); 

Выход:

diff1 = 11 
diff2 = 4294967051 
diff3 = 4294967051 

Я подозреваю diff3 бы такое же поведение, как diff1, но это на самом деле так же, как diff2.

Так почему это происходит? Насколько я могу судить, компилятор должен вычитать два 8-битных значения, а затем кастинг на 32-битный, но это явно не так. Это как-то связано со спецификацией того, как static_cast ведет себя в выражении?

+3

Это пример * интегрального продвижения * через * обычные арифметические преобразования *. Он применяется из-за выражения вычитания, а не 'static_cast'. – dyp

+0

Почему вы думаете, что diff2 будет таким же, как diff1? diff2 явно выполняет вычитание с использованием двух 'uint32_t'. Что касается результата diff3 - компилятор решил, что это правильная вещь (она есть, это даст вам самый правильный ответ). Если вы хотите, чтобы он сделал что-то еще, вам придется это рассказать. – mbgda

+0

@mbgda Они ожидают, что diff3 и diff1 будут одинаковыми. –

ответ

8

Для большинства арифметических операторов (включая -) операнды подвергаются обычным арифметическим преобразованиям . Одно из этих преобразований заключается в том, что любое значение типа, более узкого, чем int, составляет int. (Стандартная ссылка: [expr]/10).

Таким образом, выражение foo - bar становится (int)foo - (int)bar, что дает (int)-245. Затем вы наложите это на uint32_t, что даст большое положительное число.

Чтобы получить результат, который вы планируете использовать, введите uint8_t вместо uint32_t. В качестве альтернативы используйте оператор модуля % по результату отливки до uint32_t.

Это не возможно сделать расчет непосредственно в более узком, чем точность int

+1

Интересно, спасибо! Итак, если бы это было сделано на 8-битном процессоре, это дало бы результат, который я изначально ожидал? –

+1

@KeytarHero в стандарте C++ указывает, что 'int' не менее 16 бит (на 8-битном процессоре тогда компилятор должен будет использовать некоторые пары регистров или что угодно, чтобы удовлетворить требованиям стандарта) –

4

Этот вопрос не static_cast но вычитание, операнды аддитивных операторов имеют обычные арифметические преобразования, применяемые к ним, и в этом случае интегральных акций что приводит в обеих операндах вычитания продвигаемых к Int:

static_cast<uint32_t>(foo - bar); 
         ^^^ ^^^ 

с другой стороны:

static_cast<uint8_t>(foo - bar); 

будет производить желаемый результат.

из проекта C++ стандартной секции 5.7[expr.add] говорит:

Добавка операторы + и - группа слева направо. Обычные арифметические преобразования выполняются для операндов арифметики или типа перечисления.

это приводит к интегральным поощрений, раздел 5[выражение] говорит:

В противном случае, интегральные акции (4.5), должны быть выполнены на обоих операндов

, что приводит в обоих операндах, преобразованных в int, раздел 4.5[conv.пром] говорит:

prvalue целого типа, кроме BOOL, char16_t, char32_t или wchar_t которого целое число преобразования ранга (4.13) меньше, чем ранг Int может быть преобразован в prvalue типа int, если int может представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в prvalue типа unsigned int.

, а затем static_cast к uint32_t применяется, что приводит к конверсии, которая определяется следующим образом в разделе 4.7[conv.integral]:

Если тип назначения без знака, результирующее значение представляет собой наименьшее целое число без знака, совпадающее с источником integer (по модулю 2n, где n - количество бит, используемых для представления неподписанного типа). [

Рассмотрены вопросы Why must a short be converted to an int before arithmetic operations in C and C++? объясняет, почему типы меньше, чем Int продвинуты для арифметических операций.