C语言 在 C 中符号扩展九位数字

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5814072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 08:30:39  来源:igfitidea点击:

Sign extend a nine-bit number in C

c

提问by Chris Long

I have a short, instr, that looks like this:

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

1110xxx111111111

I need to pull out bits 0-9, which I do with (instr & 0x1FF). This quantity is then stored in a new short. The problem is that when this occurs, it becomes 0x0000000111111111, not 0x1111111111111111like I want. How can I fix this? Thanks!

我需要拉出位 0-9,我用(instr & 0x1FF). 然后将该数量存储在新的空头中。问题是,当这种情况发生时,就变成0x0000000111111111,不是0x1111111111111111像我想要的。我怎样才能解决这个问题?谢谢!

EDIT

编辑

Here's the code:

这是代码:

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

This is a simulator that reads in assembly. stateis the machine, regs[]are the registers and pcis the address of the current instruction in mem[].

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

This is fine if the last nine bits represent a positive number, but if they're representing -1, it's stored as all 1's, which is interpreted as a positive value by my code.

如果最后九位表示正数,这很好,但如果它们表示 -1,则存储为全 1,我的代码将其解释为正值。

回答by Ben Hymanson

Assuming a short is 16 bits:

假设 short 是 16 位:

You can do it manually: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0). This tests the sign bit (the uppermost bit you are retaining, 0x100) and sets all the bits above it if the sign bit is set. You can extend this to 5 bits by adapting the masks to 0x1F, 0x10and 0xFFE0, being the lower 5 bits, the 5th bit itself and all the bits 5-16 respectively.

您可以手动完成: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0)。这将测试符号位(您保留的最高位0x100),如果设置了符号位,则设置其上方的所有位。您可以通过将掩码调整为0x1F,0x10和将其扩展到 5 位0xFFE0,分别是低 5 位、第 5 位本身和所有 5-16 位。

Or you can find some excuse to assign the bits to the upper part of a signed short and shift them down (getting a sign-extension in the process): short x = (instr & 0x1FF) << 7; x >>= 7;The latter may actually end up being more straightforward in assembly and will not involve a branch. If instris signedthis can be done in a single expression: (instr & 0x1FF) << 7 >> 7. Since that already removes the upper bits it simplifies to instr << 7 >> 7. Replace 7 with 11 for 5 bits (16-5).

或者您可以找到一些借口将位分配给有符号短的上半部分并将它们向下移动(在此过程中获得符号扩展):short x = (instr & 0x1FF) << 7; x >>= 7;后者实际上可能最终在汇编中更加直接并且不会涉及分支. 如果instr签名,则可以在单个表达式中完成: (instr & 0x1FF) << 7 >> 7. 由于这已经删除了高位,因此简化为instr << 7 >> 7. 将 7 替换为 11 表示 5 位 (16-5)。

回答by nimrodm

* No branching required *

* 无需分支 *

See http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtendfor a list of very useful bit hacks. Specifically, sign extending a number is as simple as:

请参阅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; 

You may need to clear the uppermost bits of 'x' if they are not zero ( x = x & ((1U << b) - 1);) before using the above procedure.

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

If the number of bits 'b' is known at compile time (e.g., 5 bits in your case) there's even a simpler solution (this might trigger a specific sign-extend instruction if the processor supports it and the compiler is clever enough):

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

struct {signed int x:5;} s;
r = s.x = x;

回答by rlibby

(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7))

How does it work? It selects your sign bit and shifts it to the 2's position. This is used to generate either the value 1 (if your sign bit was absent) or -1 (if your sign bit was present).

它是如何工作的?它选择您的符号位并将其移动到 2 的位置。这用于生成值 1(如果您的符号位不存在)或 -1(如果您的符号位存在)。

This solution is branchless and does not depend on undefined behavior.

此解决方案是无分支的,不依赖于未定义的行为。

回答by SurrealWombat

Just bumped into this looking for something else, maybe a bit late, but maybe it'll be useful for someone else. AFAIAC all C programmers should start off programming assembler.

刚刚碰到这个寻找其他东西,也许有点晚了,但也许它对其他人有用。AFAIAC 所有 C 程序员都应该从汇编程序开始。

Anyway sign extending is much easier than the other 2 proposals. Just make sure you are using signed variables and then use 2 shifts.

无论如何,符号扩展比其他 2 个提案要容易得多。只要确保您使用的是带符号的变量,然后使用 2 个班次。

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;
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;

If the variable is signed then the C compiler translates >> to Arithmetic Shift Right which preserved sign. This behaviour is platform independent.

如果变量有符号,则 C 编译器将 >> 转换为保留符号的算术右移。此行为与平台无关。

So, assuming that instr starts of with 0x1ff then we have, << 7 will SL (Shift Left) the value so instr is now 0xff80, then >> 7 will ASR the value so instr is now 0xffff.

因此,假设 instr 以 0x1ff 开头,那么我们有,<< 7 将 SL(左移)值,因此 instr 现在是 0xff80,然后 >> 7 将 ASR 值,因此 instr 现在是 0xffff。

回答by Hoa Long Tam

I'm not sure how you're getting 13 1 bits after masking with 0x1ff, but this should sign-extend a 9-bit number into a 16-bit short. Not pretty (or particularly efficient), but it works:

我不确定使用 屏蔽后如何获得 13 个 1 位0x1ff,但这应该将 9 位数字符号扩展为 16 位短数字。不漂亮(或特别有效),但它有效:

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

Mask out the sign bit, shift to the 1 position to get 0/1. Multiply this by the the upper bits, if the sign is 1, then the 9-bit number will be OR'ed with 0xfe, which will set all the upper bits to 1.

屏蔽符号位,移到 1 位置得到 0/1。将此乘以高位,如果符号为 1,则 9 位数字将与 进行或运算0xfe,这会将所有高位设置为 1。

回答by Kevin Thibedeau

This is more of a refinement of previous answers but no fully generic solution has been presented so far. This macro will sign extend a value vwith sbindicating the 0-based bit number of the sign bit.

这更多是对先前答案的改进,但到目前为止还没有提出完全通用的解决方案。这个宏将符号扩展一个值vsb指示符号位的从 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)

It uses branching to maximize portability across platforms that lack a hardware multiply or barrel shifter.

它使用分支来最大化跨平台的可移植性,这些平台缺少硬件乘法或桶形移位器。

回答by dsula

An easier solution is this, for xbeing a 5-bit 2's complement number, look:

一个更简单的解决方案是,x作为一个5 位 2的补码,请看:

z = (x^16)-16