Расширение знака от 16 до 32 бит в C - PullRequest
6 голосов
/ 02 июня 2011

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

int signExtension(int instr) {
    int value = (0x0000FFFF & instr);
    int mask = 0x00008000;
    int sign = (mask & instr) >> 15;
    if (sign == 1)
        value += 0xFFFF0000;
    return value;
}

Инструкция (instr) - 32 бита, и внутри нее у меня есть 16-битное число.

Ответы [ 5 ]

17 голосов
/ 02 июня 2011

Почему не так с:

int16_t s = -890;
int32_t i = s;  //this does the job, doesn't it?
12 голосов
/ 02 июня 2011

что плохого в использовании встроенных типов?

int32_t signExtension(int32_t instr) {
    int16_t value = (int16_t)instr;
    return (int32_t)value;
}

или еще лучше (это может сгенерировать предупреждение, если передано int32_t)

int32_t signExtension(int16_t instr) {
    return (int32_t)instr;
}

или, несмотря на этоимеет значение, замените signExtension(value) на ((int32_t)(int16_t)value)

, очевидно, вам необходимо включить <stdint.h> для типов данных int16_t и int32_t.

7 голосов
/ 06 ноября 2012

Просто наткнулся на это в поисках чего-то другого, может быть, немного поздно, но, возможно, это будет полезно для кого-то еще. AFAIAC все программисты на С должны начинать программировать на ассемблере.

В любом случае расширение знака намного проще, чем предложения. Просто убедитесь, что вы используете подписанные переменные, а затем используйте 2 смены.

long value;   // 32 bit storage
value=0xffff; // 16 bit 2's complement -1, value is now 0x0000ffff
value = ((value << 16) >> 16); // value is now 0xffffffff

Если переменная подписана, то компилятор C переводит >> в Arithmetic Shift Right, который сохраняет знак. Это поведение не зависит от платформы.

Итак, предполагая, что значение начинается с 0x1ff, тогда мы имеем, << 16 будет SL (Shift Left) значение, так что instr теперь 0xff80, тогда >> 16 будет ASR, так что instr теперь 0xffff.

Если вы действительно хотите повеселиться с макросами, попробуйте что-то вроде этого (синтаксис работает в GCC, а в MSVC не пробовал).

#include <stdio.h>

#define INT8 signed char
#define INT16 signed short
#define INT32 signed long
#define INT64 signed long long
#define SIGN_EXTEND(to, from, value) ((INT##to)((INT##to)(((INT##to)value) << (to - from)) >> (to - from)))

int main(int argc, char *argv[], char *envp[])
{
    INT16 value16 = 0x10f;
    INT32 value32 = 0x10f;
    printf("SIGN_EXTEND(8,3,6)=%i\n", SIGN_EXTEND(8,3,6));
    printf("LITERAL         SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,0x10f));
    printf("16 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,value16));
    printf("32 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,value32));

    return 0;
}

Создает следующий вывод:

SIGN_EXTEND(8,3,6)=-2
LITERAL         SIGN_EXTEND(16,9,0x10f)=-241
16 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=-241
32 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=-241
6 голосов
/ 02 июня 2011

Попробуйте:

int signExtension(int instr) {
    int value = (0x0000FFFF & instr);
    int mask = 0x00008000;
    if (mask & instr) {
        value += 0xFFFF0000;
    }
    return value;
}
1 голос
/ 22 августа 2018

Люди указали на кастинг и сдвиг влево, а затем арифметический сдвиг вправо.Другой способ, который не требует ветвления:

(0xffff & n ^ 0x8000) - 0x8000

Если старшие 16 битов уже равны нулю:

(n ^ 0x8000) - 0x8000

• Вики сообщества, как идея «Совокупные магические алгоритмы, расширение знака»

...