Расширение знака, сложение и вычитание двоичного в C
Как бы я пошел о реализации расширения знака от 16 бит до 32 бит в коде C?
Я должен использовать побитовые операторы. Мне также нужно сложить и вычесть; Может кто-то указать мне верное направление? Я сделал первые 4, но остальное смутил. Я должен включить for
Цикл где-то в одном случае.
Мне не разрешено использовать любые арифметические операторы (+
, -
, /
, *
) и нет if
заявления.
Вот код для оператора switch, который я сейчас редактирую:
unsigned int csc333ALU(const unsigned int opcode,
const unsigned int argument1,
const unsigned int argument2) {
unsigned int result;
switch(opcode) {
case(0x01): // result = NOT argument1
result = ~(argument1);
break;
case(0x02): // result = argument 1 OR argument 2
result = argument1 | argument2;
break;
case(0x03): // result = argument 1 AND argument 2
result = argument1 & argument2;
break;
case(0x04): // result = argument 1 XOR argument 2
result = argument1 ^ argument2;
break;
case(0x05): // result = 16 bit argument 1 sign extended to 32 bits
result = 0x00000000;
break;
case(0x06): // result = argument1 + argument2
result = 0x00000000;
break;
case(0x07): // result = -argument1. In two's complement, negate and add 1.
result = 0x00000000;
break;
default:
printf("Invalid opcode: %X\n", opcode);
result = 0xFFFFFFFF;
}
3 ответа
Частичный ответ за расширение знака:
result = (argument1 & 0x8000) == 0x8000 ? 0xFFFF0000 | argument1 : argument1;
Чтобы расширить знак 16-битного числа до 32-битного, вам нужно скопировать 15-й бит в верхние биты. Наивный способ сделать это - 16 инструкций, копирование бита 15 в бит 16, затем 17, затем 18 и так далее. Но вы можете сделать это более эффективно, используя ранее скопированные биты и удваивая количество битов, которые вы копировали каждый раз, вот так:
unsigned int ext = (argument1 & 0x8000U) << 1;
ext |= ext << 1;
ext |= ext << 2;
ext |= ext << 4;
ext |= ext << 8;
result = (argument1 & 0xffffU) | ext;
Чтобы добавить два 32-разрядных числа "вручную", вы можете просто сделать это по крупицам.
unsigned carry = 0;
result = 0;
for (int i = 0; i < 32; i++) {
// Extract the ith bit from argument1 and argument 2.
unsigned a1 = (argument1 >> i) & 1;
unsigned a2 = (argument2 >> i) & 1;
// The ith bit of result is set if 1 or 3 of a1, a2, carry is set.
unsigned v = a1 ^ a2 ^ carry;
result |= v << i;
// The new carry is 1 if at least two of a1, a2, carry is set.
carry = (a1 & a2) | (a1 & carry) | (a2 & carry);
}
Вычитание работает практически с тем же кодом: a - b
такой же как a + (~b+1)
в двух дополнениях арифметика. Поскольку вы не можете просто добавить 1, вы можете добиться того же, инициализируя carry
в 1
вместо 0
,
unsigned carry = 1;
result = 0;
for (int i = 0; i < 32; i++) {
unsigned a1 = (argument1 >> i) & 1;
unsigned a2 = (~argument2 >> i) & 1;
unsigned v = a1 ^ a2 ^ carry;
result |= v << i;
carry = (a1 & a2) | (a1 & carry) | (a2 & carry);
}
Чтобы найти два дополнения без отрицания, применяются аналогичные идеи. Побитовое отрицание, а затем добавить 1
, Добавление 1
проще, чем добавить argument2
поэтому код, соответственно, проще.
result = ~argument1;
unsigned carry = 1;
for (int i = 0; i < 32 && carry; i++) {
carry &= (result >> i);
result |= (1 << i);
}
Получить расширение знака от short int
в int
....
short int iShort = value;
int i = iShort; // compiler automatically creates code that performs sign extension
Примечание: собирается из i
iShort сгенерирует компиляцию waring.
однако для других ситуаций...
не нужно сравнивать, &
приведет к тому, что один бит будет либо 0, либо 1, и обязательно приведем части вычисления к типу int
int i = (short int argument&0x8000)? (int)(0xFFFF000 | (int)argument) : (int)argument;