Преобразовать со знаком int переменной размера бит - PullRequest
2 голосов
/ 21 марта 2019

У меня есть количество бит (количество бит может измениться) в беззнаковом int (uint32_t).Например (в примере 12 битов):

uint32_t a = 0xF9C;

Биты представляют целое число со знаком этой длины.В этом случае десятичное число должно быть -100.Я хочу сохранить переменную в знаковой переменной и получает это фактическое значение.Если я просто использую:

int32_t b = (int32_t)a;

, это будет просто значение 3996, поскольку оно приводится к (0x00000F9C), но на самом деле оно должно быть (0xFFFFFF9C)

Я знаю один способсделайте это:

union test
{
    signed temp :12;
}; 
union test x;
x.temp = a;
int32_t result = (int32_t) x.temp;

Теперь я получаю правильное значение -100

Но есть ли лучший способ сделать это?Мое решение не очень гибкое, так как я упомянул, что число битов может варьироваться (от 1 до 64 бит).

Ответы [ 5 ]

3 голосов
/ 21 марта 2019

Но есть ли лучший способ сделать это?

Ну, зависит от того, что вы подразумеваете под "лучше".Пример ниже показывает более гибкий способ сделать это, поскольку размер битового поля не фиксирован.Если в вашем случае использования требуются биты другого размера, вы можете считать это «лучшим» способом.

unsigned sign_extend(unsigned x, unsigned num_bits)
{
    unsigned f = ~((1 << (num_bits-1)) - 1);
    if (x & f)  x = x | f;
    return x;
}


int main(void)
{
    int x = sign_extend(0xf9c, 12);
    printf("%d\n", x);

    int y = sign_extend(0x79c, 12);
    printf("%d\n", y);
}

Вывод:

-100
1948
1 голос
/ 21 марта 2019

Свободный способ подписать расширение битового поля (Генри С. Уоррен-младший, CACM v20 n6, июнь 1977 г.) заключается в следующем:

// value i of bit-length len is a bitfield to sign extend
// i is right aligned and zero-filled to the left
sext = 1 << (len - 1);
i = (i ^ sext) - sext;

ОБНОВЛЕНИЕ на основе @ комментария Лундина

Вот проверенный код (отпечатки -100):

#include <stdio.h>
#include <stdint.h>

int32_t sign_extend (uint32_t x, int32_t len)
{
    int32_t i = (x & ((1u << len) - 1)); // or just x if you know there are no extraneous bits
    int32_t sext = 1 << (len - 1);
    return (i ^ sext) - sext;
}

int main(void)
{
    printf("%d\n", sign_extend(0xF9C, 12));
    return 0;
}
0 голосов
/ 22 марта 2019

Разумный, переносимый и эффективный способ сделать это - просто замаскировать часть данных, а затем заполнить все остальное 0xFF ..., чтобы получить правильное представление дополнения 2.Вы должны знать, сколько битов являются частью данных.

  • Мы можем замаскировать данные с помощью (1u << data_length) - 1.
  • В этом случае с data_length = 8 маска данных становится 0xFF.Давайте назовем это data_mask.
  • Таким образом, часть данных числа равна a & data_mask.
  • Остальная часть числа должна быть заполнена нулями.То есть все, что не является частью маски данных.Для этого просто наберите ~data_mask.
  • C код: a = (a & data_mask) | ~data_mask.Теперь a является правильным 32-битным дополнением 2.

Пример:

#include <stdio.h>
#include <inttypes.h>

int main(void) 
{
  const uint32_t data_length = 8;
  const uint32_t data_mask = (1u << data_length) - 1;

  uint32_t a = 0xF9C;
  a = (a & data_mask) | ~data_mask;

  printf("%"PRIX32 "\t%"PRIi32, a, (int32_t)a);
}

Вывод:

FFFFFF9C        -100

Это зависит от int, являющегося 32биты 2 дополняют, но в остальном полностью переносимы.

0 голосов
/ 21 марта 2019

Это решение вашей проблемы:

int32_t sign_extend(uint32_t x, uint32_t bit_size)
{
    // The expression (0xffffffff << bit_size) will fill the upper bits to sign extend the number.
    // The expression (-(x >> (bit_size-1))) is a mask that will zero the previous expression in case the number was positive (to avoid having an if statemet).
    return (0xffffffff << bit_size) & (-(x >> (bit_size-1))) | x;
}
int main()
{

    printf("%d\n", sign_extend(0xf9c, 12)); // -100
    printf("%d\n", sign_extend(0x7ff, 12)); // 2047

    return 0;
}
0 голосов
/ 21 марта 2019

Это опирается на определенную реализацию поведения расширения знака при смещении вправо отрицательных целых чисел со знаком.Сначала вы сдвигаете целое число без знака до конца, пока знаковый бит не станет MSB, а затем приведете его к целому числу со знаком и вернетесь назад:

#include <stdio.h>
#include <stdint.h>

#define NUMBER_OF_BITS 12

int main(void) {
    uint32_t x = 0xF9C;
    int32_t y = (int32_t)(x << (32-NUMBER_OF_BITS)) >> (32-NUMBER_OF_BITS);

    printf("%d\n", y);

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...