我正在我的简单 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/