符号在C中扩展一个九位数

Chr*_*ong 19 c

我有一个短片,instr看起来像这样:

1110xxx111111111
Run Code Online (Sandbox Code Playgroud)

我需要拔出0-9位,我这样做(instr & 0x1FF).然后将该数量存储在新的短期中.问题是,当这种情况发生时,它变得0x00000001111111110x1111111111111111喜欢我想要的.我怎样才能解决这个问题?谢谢!

编辑

这是代码:

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

这是一个读取汇编的模拟器.state是机器,regs[]是寄存器,pc是当前指令的地址mem[].

如果最后9位表示正数,则可以正常,但如果它们表示-1,则将其存储为全1,这由我的代码解释为正值.

Ben*_*son 23

假设一个短是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).

  • 在后者,你不能只做`(instr&0x1FF << 7)>> 7`? (3认同)
  • @Ben Jackson,@ Steve314正确地担心签署的右移.签名右移的结果不是由标准定义的.当然你的第一个解决方案是有效的 - 只要你接受OP的前提,即'short`是16位(也不能保证). (3认同)
  • +1,但是向左移动然后向右移动应该可以工作(只要您使用有符号类型),但是如果一些相对晦涩的编译器(可能在嵌入式平台上)错误地优化它,我不会感到惊讶,假设这两个班次将抵消并且没有注意到符号扩展的副作用。我提到这一点是因为当从硬件寄存器中提取值时,代码风格似乎最有可能出现在嵌入式平台上。 (2认同)

nim*_*odm 12

*无需分支*

请参阅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; 
Run Code Online (Sandbox Code Playgroud)

x = x & ((1U << b) - 1);在使用上述过程之前,如果它们不是零(),您可能需要清除'x'的最高位.

如果在编译时知道位数'b'(例如,在你的情况下是5位),那么甚至有一个更简单的解决方案(如果处理器支持它并且编译器足够聪明,这可能会触发特定的符号扩展指令):

struct {signed int x:5;} s;
r = s.x = x;
Run Code Online (Sandbox Code Playgroud)