Расширение знака, сложение и вычитание двоичного в 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;
Другие вопросы по тегам