C 中九位数字的符号扩展

发布于 2024-11-04 06:13:48 字数 662 浏览 0 评论 0原文

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

1110xxx111111111

我需要拉出位 0-9,这是我使用 (instr & 0x1FF) 完成的。然后将该数量存储在新的短路中。问题是,当这种情况发生时,它变成了 0x0000000111111111,而不是像我想要的那样 0x1111111111111111 。我该如何解决这个问题?谢谢!

编辑

这是代码:

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

这是一个读取汇编的模拟器。 state 是机器,regs[] 是寄存器,pcmem[].

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

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

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 0x1111111111111111 like I want. How can I fix this? Thanks!

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. state is the machine, regs[] are the registers and pc is the address of the current instruction in 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

云淡风轻 2024-11-11 06:13:48

* 无需分支 *

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

* No branching required *

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

/* 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.

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):

struct {signed int x:5;} s;
r = s.x = x;
﹂绝世的画 2024-11-11 06:13:48

假设短路是 16 位:

您可以手动执行:(instr & 0x1FF) | ((指令&0x100)?0xFE00:0)。这会测试符号位(您保留的最高位,0x100)并设置其上方的所有位(如果设置了符号位)。您可以通过将掩码调整为 0x1F0x100xFFE0(即低 5 位、第 5 位本身和分别为所有位 5-16。

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

Assuming a short is 16 bits:

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, 0x10 and 0xFFE0, being the lower 5 bits, the 5th bit itself and all the bits 5-16 respectively.

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 instr is signed this 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).

千仐 2024-11-11 06:13:48
(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7))

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

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

(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).

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

浅浅 2024-11-11 06:13:48

这更多的是对以前答案的改进,但到目前为止还没有提出完全通用的解决方案。该宏将用 sb 指示符号位的从 0 开始的位数来对值 v 进行符号扩展。

#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)

它使用分支来最大限度地提高跨缺乏硬件乘法器或桶式移位器的平台的可移植性。

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 v with sb indicating the 0-based bit number of the sign bit.

#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.

甜是你 2024-11-11 06:13:48

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

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

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

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:

(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.

青萝楚歌 2024-11-11 06:13:48

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

无论如何,符号扩展比其他两个提案要容易得多。只需确保您使用的是有符号变量,然后使用 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;

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

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

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.

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

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.

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.

寂寞清仓 2024-11-11 06:13:48

一个更简单的解决方案是这样的,因为 x5 位 2 的补数,请查看:

z = (x^16)-16

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

z = (x^16)-16
旧伤慢歌 2024-11-11 06:13:48

不是最优的,但可以说是易于阅读的解决方案

要对 9 位值进行符号扩展,您可以将

(instr & 0x1FF)

... 替换为 ...

((instr & 0x1FF) | -(instr & 0x100))

((instr & 0x0FF) | -(instr & 0x100))

说明

在 2 补码算术中,(instr & 0x100) 将隔离符号位,并用 -(instr & 0x100) 对其求反将有效地对隔离的符号进行符号扩展符号位。最后将符号扩展符号位与原始值(带或不带符号位)进行“或”以获得符号扩展:((instr & 0x1FF) | -(instr & 0x100))((指令&0x0FF)|-(指令&0x100))

Not the most optimal, but arguably easy to read solution:

To sign extend the 9-bit value, you can replace

(instr & 0x1FF)

... with ...

((instr & 0x1FF) | -(instr & 0x100))

or

((instr & 0x0FF) | -(instr & 0x100))

.

Explanation

In 2s-complement arithmetic, (instr & 0x100) will isolate the sign bit and negating it with -(instr & 0x100) will effectively sign-extend the isolated sign bit. Finally OR the sign-extended sign bit with the original value (with or without its sign bit) to obtain the sign extension: ((instr & 0x1FF) | -(instr & 0x100)) or ((instr & 0x0FF) | -(instr & 0x100)).

云之铃。 2024-11-11 06:13:48

以下是我如何将 9 位扩展为 16 位。

int16_t x = (int16_t)(instr << 6) >> 6;

或者像这样:

int16_t x = (instr & 0x100) ? (int16_t)inst - 0x200 : (int16_t)inst;

Here is how I would sign extend 9 bits to 16 bits.

int16_t x = (int16_t)(instr << 6) >> 6;

Or like this:

int16_t x = (instr & 0x100) ? (int16_t)inst - 0x200 : (int16_t)inst;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文