c - 从 8 位值转换时如何对 9 位值进行符号扩展?

标签 c int bit-manipulation signed stdint

我正在我的简单 VM 中实现一个相对分支函数。

基本上,我得到了一个 8 位的相对值。然后我将其左移 1 位,使其成为 9 位值。因此,例如,如果您说“分支 +127”,这实际上意味着 127 条指令,因此会将 256 添加到 IP。

我当前的代码如下所示:

uint8_t argument = 0xFF; //-1 or whatever
int16_t difference = argument << 1;
*ip += difference; //ip is a uint16_t

不过,我不认为差异会被检测为小于 0。我对如何签名到未签名的作品感到生疏。除此之外,我不确定在 case 参数是 -1 或 -2 或其他东西的情况下,是否可以正确地从 IP 中减去差异。

基本上,我想要满足这些“测试”的东西

//case 1
argument = -5
difference -> -10
ip = 20 -> 10 //ip starts at 20, but becomes 10 after applying difference

//case 2
argument = 127 (must fit in a byte)
difference -> 254
ip = 20 -> 274

希望这能让它更清楚一点。

无论如何,我怎样才能便宜地做到这一点?我看到一个类似问题的“解决方案”,但它涉及 split 。我正在使用速度较慢的嵌入式处理器(假设没有有效的乘法和除法方法),所以这是我想避免的一件大事。

最佳答案

澄清一下:您担心左移一个负 8 位数会使它看起来像一个正的 9 位数?只需在左移前用初始数字的符号位填充前 9 位:

diff = 0xFF;
int16 diff16=(diff + (diff & 0x80)*0x01FE) << 1;

现在您的 diff16 已签名 2*diff

正如 Richard J Ross III 所指出的,您可以使用条件分支避免乘法(如果这在您的平台上成本很高):

int16 diff16 = (diff + ((diff & 0x80)?0xFF00:0))<<1;

如果您担心事情会停留在范围内(“未定义的行为”),您可以这样做

int16 diff16 = diff;
diff16 = (diff16 | ((diff16 & 0x80)?0x7F00:0))<<1;

这绝不会产生超出范围的数字。

不过,最干净的解决方案似乎是“强制转换”:

diff16 = (signed char)diff; // recognizes and preserves the sign of diff
diff16 = (short int)((unsigned short)diff16)<<1; // left shift, preserving sign

这会产生预期的结果,因为编译器会自动处理第一行中的符号位(因此不需要掩码);在第二行中,它对一个无符号整数进行了左移(根据标准明确定义了溢出);最终转换回 short int 确保数字被正确解释为负数。我相信在这种形式下,构造永远不会是“未定义的”。

关于c - 从 8 位值转换时如何对 9 位值进行符号扩展?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15939833/

相关文章:

c - 将结构数组传递给函数会更改数组的大小

c - 带有 `df` 的可用 block

java - 将用空格分隔的整数读入 int[] 数组

C++ 困惑。从文本文件中读取整数。转换为 ASCII

c++ - boolean 变量按位非

c++ - for循环的增量语句中的奇数位运算符

c - 无符号为十六进制数字

c - 如何在数组内打印二进制数?

c - 如何将结构从函数返回到结构指针?

java - 将整数编码为字节字符串