Умножение в языке C/С++: знаковое и беззнаковое

Задавашись вопросом, что же всё-таки получается в результате умножения двух 8-битных операндов в случае, когда один или оба операнда представляют собой числа со знаком (signed) и без знака (unsigned), я составил небольшую тестовую программку, которая распечатывает результат вычисления для разных типов операндов и разных типов результатов.

Вот текст этой программы (исходный код):

#include <stdio.h>

typedef unsigned char uint8_t;

typedef char int8_t;

int main(void)

{

uint8_t x1, y1;

int8_t x2, y2;

short z1, z2, z3, z4;

unsigned short z5, z6, z7, z8;

printf("unsigned char\tunsigned char\tsigned char\tsigned char\t");

printf("signed short\tsigned short\tsigned short\tsigned short\t");

printf("unsigned short\tunsigned short\tunsigned short\tunsigned short\n");

printf("x1\ty1\tx2\ty2\t");

printf("z1=x1*y1\tz2=x1*y2\tz3=x2*y1\tz4=x2*y2\t");

printf("z5=x1*y1\tz6=x1*y2\tz7=x2*y1\tz8=x2*y2\n");

for(int n = 0; n<256; ++n) {

x1 = y1 = x2 = y2 = n;

z1 = x1*y1;

z2 = x1*y2;

z3 = x2*y1;

z4 = x2*y2;

z5 = x1*y1;

z6 = x1*y2;

z7 = x2*y1;

z8 = x2*y2;

printf("%d\t%d\t%d\t%d\t", x1, y1, x2, y2);

printf("%d\t%d\t%d\t%d\t", z1, z2, z3, z4);

printf("%d\t%d\t%d\t%d\n", z5, z6, z7, z8);

}

return 0;

}

Результат вычисления можно увидеть в таблице:

  • для процессора Intel Core2 Duo (компилятор GCC) - CSV, HTML

  • для микроконтроллера AVR ATmega168 (компилятор AVR-GCC) - CSV

Чтобы получить результаты для микроконтроллера AVR ATmega168, я портировал тестовую программу в скетч для Arduino, а на хосте использовал небольшой скрипт на языке Python.

Результаты меня с одной стороны порадовали, поскольку оказались идентичными для обоих процессоров, хотя для целочисленных вычислений было бы странно ожидать, что в них будет какая-нибудь погрешность.

С другой стороны я обратил внимание на то, как тип операнда влияет на вычисление. Это видно например при умножении 255*255 - в одном случае в результате получается беззнаковое 65025 (0xFE01, -511 со знаком), в другом - 65281 (0xFF01, -255 со знаком). Разница на мой взгляд довольно существенная.

Автор: Андрей Шаройко <vanyamboe@gmail.com>