我在我的简单VM中实现了一个相对分支函数。
基本上,我得到了一个8位的相对值。然后向左移动1位,使其成为9位值。例如,如果你说“branch+127”,这实际上意味着127条指令,因此会给IP增加256条。
我当前的代码如下:

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

不过,我不认为用这个方法可以检测到小于0的差异。我对“签名到未签名”的工作方式感到生疏。除此之外,在case参数是say-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

希望这能更清楚一点。
不管怎样,我怎么能便宜一点呢?我看到了一个类似问题的“解决方案”,但涉及到部门。我使用的是速度慢的嵌入式处理器(假设没有有效的乘法和除法),所以这是我想避免的一件大事。

最佳答案

澄清:你担心左移一个负的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

这会产生预期的结果,因为编译器会自动处理第一行中的符号位(因此不需要掩码);而在第二行中,编译器会对无符号int执行左移位(溢出是根据标准定义的);最后返回到short int可以确保数字正确解释为负数。我相信在这种形式下,结构从来就不是“未定义”的。

10-05 22:06