Знак расширения девятизначного числа в C - PullRequest
19 голосов
/ 28 апреля 2011

У меня короткая позиция, instr, которая выглядит так:

1110xxx111111111

Мне нужно вытащить биты 0-9, что я делаю с (instr & 0x1FF). Это количество затем сохраняется в новом шорт. Проблема в том, что когда это происходит, оно становится 0x0000000111111111, а не 0x1111111111111111, как я хочу. Как я могу это исправить? Спасибо!

EDIT

Вот код:

short instr = state->mem[state->pc];
unsigned int reg = instr >> 9 & 7; // 0b111
state->regs[reg] = state->pc + (instr & 0x1FF);

Это симулятор, который читает на ассемблере. state - это машина, regs[] - регистры, а pc - адрес текущей инструкции в mem[].

Это нормально, если последние девять бит представляют положительное число, но если они представляют -1, оно сохраняется как все 1, что интерпретируется моим кодом как положительное значение.

Ответы [ 7 ]

23 голосов
/ 28 апреля 2011

Предполагается, что короткое замыкание составляет 16 бит:

Вы можете сделать это вручную: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0).Это проверяет бит знака (самый старший бит, который вы сохраняете, 0x100) и устанавливает все биты над ним, если бит знака установлен.Вы можете расширить это до 5 битов, адаптируя маски к 0x1F, 0x10 и 0xFFE0, которые являются младшими 5 битами, самим 5-м битом и всеми битами 5-16 соответственно.Вы можете найти какое-то оправдание, чтобы назначить биты верхней части подписанного короткого замыкания и сдвинуть их вниз (получая расширение знака в процессе): short x = (instr & 0x1FF) << 7; x >>= 7; Последнее может фактически оказаться более простым в сборке и не потребуетотделение.Если instr равно со знаком , это можно сделать в одном выражении: (instr & 0x1FF) << 7 >> 7.Поскольку это уже удаляет верхние биты, оно упрощается до instr << 7 >> 7.Замените 7 на 11 для 5 бит (16-5).

12 голосов
/ 18 июля 2013

* Нет необходимости в ветвлении *

См. http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend для списка очень полезных битовых хаков.В частности, знак, расширяющий число, так же прост:

/* generate the sign bit mask. 'b' is the extracted number of bits */
int m = 1U << (b - 1);  

/* Transform a 'b' bits unsigned number 'x' into a signed number 'r' */
int r = (x ^ m) - m; 

Вам может потребоваться очистить старшие биты «x», если они не равны нулю (x = x & ((1U << b) - 1);), перед использованием описанной выше процедуры.*

Если число битов «b» известно во время компиляции (например, 5 битов в вашем случае), есть даже более простое решение (это может вызвать специальную инструкцию расширения знака, если процессор поддерживает ее, а компилятордостаточно умный):

struct {signed int x:5;} s;
r = s.x = x;
4 голосов
/ 28 апреля 2011
(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7))

Как это работает?Он выбирает ваш бит знака и сдвигает его в позицию 2.Это используется для генерирования либо значения 1 (если ваш знаковый бит отсутствовал), либо -1 (если ваш знаковый бит присутствовал).

Это решение не имеет ответвлений и не зависит от неопределенного поведения.

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

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

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

</p> <pre><code>short instr = state->mem[state->pc]; unsigned int reg = (instr >> 9) & 7; // 0b111 instr &= 0x1ff; // get lower 9 bits instr = ((instr << 7) >> 7); // sign extend state->regs[reg] = state->pc + instr;

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

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

2 голосов
/ 28 апреля 2011

Я не уверен, как вы получаете 13 1 бит после маскирования с помощью 0x1ff, но это должно знамение - расширить 9-битное число в 16-битное короткое. Не очень (или особенно эффективно), но это работает:

(instr & 0x1ff) | (0xfe00 * ((instr & 0x100) >> 8))

Замаскируйте бит знака, сдвиньте в положение 1, чтобы получить 0/1. Умножьте это на верхние биты, если знак равен 1, тогда 9-битное число будет ИЛИ с 0xfe, что установит все старшие биты на 1.

1 голос
/ 17 октября 2017

Более простое решение: для x, являющегося дополнительным номером 5-бит 2 , смотрите:

z = (x^16)-16
1 голос
/ 27 июля 2015

Это скорее уточнение предыдущих ответов, но до сих пор не было представлено ни одного полностью общего решения. Этот макрос будет расширять знак v значением sb, указывающим номер бита знака на основе 0.

#define SIGNEX(v, sb) ((v) | (((v) & (1 << (sb))) ? ~((1 << (sb))-1) : 0))

int32_t x;

SIGNEX(x, 15); // Sign bit is bit-15 (16th from the right)
SIGNEX(x, 23); // Sign bit is bit-23 (24th from the right)

В нем используется ветвление для максимизации переносимости между платформами, в которых отсутствует аппаратное умножение или смещение бочек.

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