Как подписать расширение числа различного размера до 16 бит в C?
Число различного размера может быть либо 10-битным, либо 12-битным, либо 15-битным. Я хочу подписать расширение непосредственных битов так, чтобы 16-разрядное целое число имело то же значение, что и 10, 12 или 15-разрядное значение.
РЕДАКТИРОВАТЬ
Извините, я не опубликовал код, который я написал, это не работает. Вот:
switch (instr):
{
case IMMI_10:
int bits_to_cover = 16 - 10;
signed short amount = (instr & 15);
if (amount & (1 << bits_to_cover >> 1))
{
amount -= 1 << bits_to_cover;
}
break;
case IMMI_12:
int bits_to_cover = 16 - 12;
signed short amount = (instr & 15);
if (amount & (1 << bits_to_cover >> 1))
{
amount -= 1 << bits_to_cover;
}
break;
case IMMI_15:
int bits_to_cover = 16 - 15;
signed short amount = (instr & 15);
if (amount & (1 << bits_to_cover >> 1))
{
amount -= 1 << bits_to_cover;
}
break;
}
Это работает на специальной машине, созданной для моего класса CSE в школе. Это не работает архитектура x86. Он называется машиной CSE 410. Документация: https://courses.cs.washington.edu/courses/cse410/18sp/410machine/isaManual.html
1 ответ
Я работаю на основе того, что, например, если у вас есть 10-битное число, которое должно быть расширено до 16 бит, то вам нужно рассмотреть два случая:
zzzzzz1x xxxxxxxx
zzzzzz0x xxxxxxxx
Х - это "все равно - нужно копировать" биты в результате. Ведущие z - "все равно - перезапишут" биты. И бит, который переключается между 0 и 1 в этих двух примерах, является знаковым битом, который должен быть скопирован поверх того, что показано как начальные z. Я также предполагаю, что биты пронумерованы от 0 (младший значащий бит или младший бит) до 15 (старший значащий бит или младший бит). Если вам нужно пронумеровать биты от 1 до 16, вам нужно внести некоторые коррективы.
С учетом сигнатуры функции:
uint16_t sign_extend(uint16_t value, uint16_t bits)
мы можем определить бит знака с помощью:
uint16_t sign = (1 << (bits - 1)) & value;
Мы можем 0-расширить значение с помощью знака положительного (0) знака, и '' значение с помощью битовой маски:
00000001 11111111
Мы можем 1-расширить значение с помощью знака отрицательного (1) бита, или 'с помощью значения битовой маски:
11111110 00000000
В приведенном ниже коде я создаю вторую маску с помощью:
uint16_t mask = ((~0U) >> (bits - 1)) << (bits - 1);
и использовать побитовую инверсию для генерации другого.
Код избегает предположений о том, что происходит, когда вы сдвигаете отрицательное значение вправо. (См. Комментарий samgak.) Стандарт C говорит, что это поведение, определяемое реализацией, и обычные случаи - это "скопировать бит MSB (знак) в освобожденный бит" (он же арифметический сдвиг вправо) или "установить освобожденные биты в ноль". ' (иначе логический сдвиг вправо). Оба разрешены, но данный компилятор должен использовать один или другой. Этот код будет работать независимо от того, что делает компилятор, потому что он избегает сдвига вправо знаковых величин. (Чтобы восполнить это, предполагается, что вы можете назначить целые числа со знаком для соответствующего типа целого числа без знака и наоборот, даже если значение со знаком является отрицательным. Формально стандарт требует, чтобы он работал только для общего подмножества значений - от От 0 до <signed-type>_MAX
, но я не слышал о системах, в которых это является проблемой, тогда как я слышал о системах, в которых переключение выполняется по-разному.)
Собирая все вместе, вот функция, которую я бы использовал в тестовом жгуте:
#include <assert.h>
#include <stdint.h>
extern uint16_t sign_extend(uint16_t value, uint16_t bits);
uint16_t sign_extend(uint16_t value, uint16_t bits)
{
assert(bits > 0 && bits < 16);
uint16_t sign = (1 << (bits - 1)) & value;
uint16_t mask = ((~0U) >> (bits - 1)) << (bits - 1);
if (sign != 0)
value |= mask;
else
value &= ~mask;
return value;
}
#ifdef TEST
#include <stdio.h>
struct TestSignExtend
{
uint16_t value;
uint16_t bits;
uint16_t result;
};
static int test_sign_extend(const struct TestSignExtend *test)
{
uint16_t result = sign_extend(test->value, test->bits);
const char *pass = (result == test->result) ? "** PASS **" : "== FAIL ==";
printf("%s: value = 0x%.4X, bits = %2d, wanted = 0x%.4X, actual = 0x%.4X\n",
pass, test->value, test->bits, test->result, result);
return(result == test->result);
}
int main(void)
{
struct TestSignExtend tests[] =
{
{ 0x0123, 10, 0x0123 },
{ 0x0323, 10, 0xFF23 },
{ 0x0323, 11, 0x0323 },
{ 0x0723, 11, 0xFF23 },
{ 0x0323, 12, 0x0323 },
{ 0x0C23, 12, 0xFC23 },
{ 0x0323, 13, 0x0323 },
{ 0x1723, 13, 0xF723 },
{ 0x1323, 14, 0x1323 },
{ 0x3723, 14, 0xF723 },
{ 0x0323, 15, 0x0323 },
{ 0xC723, 15, 0xC723 },
{ 0x0123, 9, 0xFF23 },
{ 0x0223, 9, 0x0023 },
{ 0x0129, 8, 0x0029 },
{ 0x03E9, 8, 0xFFE9 },
};
enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };
int pass = 0;
for (int i = 0; i < NUM_TESTS; i++)
pass += test_sign_extend(&tests[i]);
if (pass == NUM_TESTS)
printf("PASS - All %d tests passed\n", NUM_TESTS);
else
printf("FAIL - %d tests failed out of %d run\n", NUM_TESTS - pass, NUM_TESTS);
return(pass != NUM_TESTS); /* Process logic is inverted! */
}
#endif /* TEST */
Образец вывода:
** PASS **: value = 0x0123, bits = 10, wanted = 0x0123, actual = 0x0123
** PASS **: value = 0x0323, bits = 10, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0323, bits = 11, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x0723, bits = 11, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0323, bits = 12, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x0C23, bits = 12, wanted = 0xFC23, actual = 0xFC23
** PASS **: value = 0x0323, bits = 13, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x1723, bits = 13, wanted = 0xF723, actual = 0xF723
** PASS **: value = 0x1323, bits = 14, wanted = 0x1323, actual = 0x1323
** PASS **: value = 0x3723, bits = 14, wanted = 0xF723, actual = 0xF723
** PASS **: value = 0x0323, bits = 15, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0xC723, bits = 15, wanted = 0xC723, actual = 0xC723
** PASS **: value = 0x0123, bits = 9, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0223, bits = 9, wanted = 0x0023, actual = 0x0023
** PASS **: value = 0x0129, bits = 8, wanted = 0x0029, actual = 0x0029
** PASS **: value = 0x03E9, bits = 8, wanted = 0xFFE9, actual = 0xFFE9
PASS - All 16 tests passed
Я сделал преднамеренную ошибку в одном из тестов после того, как все прошло с первого хода, просто чтобы убедиться, что сбои будут обнаружены.
В вашем коде вы можете использовать его так:
signed short value = …;
switch (instr):
{
case IMMI_10:
value = sign_extend(value, 10);
break;
case IMMI_12:
value = sign_extend(value, 12);
break;
case IMMI_15:
value = sign_extend(value, 15);
break;
default:
assert(0 && "can't happen!");
}
Если случай этикетки IMMI_10
, IMMI_12
а также IMMI_15
имеют значения 10
, 12
, 15
, тогда вы можете избежать переключения и просто использовать присваивание:
signed short value = …; // Get the value from somewhere
value = sign_extend(value, instr);