我有两个char,我希望我的程序可以作为一个2的补码值。例如,如果我有:
char i = 0xFF;
char j = 0xF0;
int k = ((i<<8) | j);
然后我希望C将k作为2的补码(所以-16代替65520)。我该怎么做?
答案 0 :(得分:2)
你想设置所有最重要的位,除了较低的16到1.这样的东西应该这样做。
k |= (-1&~0xFFFF);
也就是说,如果您的编译器将字符解释为已签名(我认为最多),则k已经是-16。
此外,对于带符号的字符,如果 j 设置了最高有效位,则结果通常是不正确的(就像在这种情况下一样)。在评估表达式期间, j 将被类型提升为负数,并设置所有最高有效位。当这样的数字与表达式的其余部分进行“或”运算时,这些位将覆盖其他所有内容。它只适用于这种情况,因为 i 已经设置了所有的位,所以它没有任何区别。
答案 1 :(得分:2)
int
相比, unsigned int
变量始终被解释为二进制补码。你的价值不是-16:)
运行代码后,k将是(假设32位整数宽度)
k == 0x0000FFF0 // k == 65520
,而:
-16 == 0xFFFFFFF0
为了克服这个问题,你可以做的是事先将k
的所有位设置为1
int k = -1; // k == 0xFFFFFFFF
k &= ((i << 8) | j); // k == 0xFFFFFFF0
答案 2 :(得分:1)
您正在使用编译器编译代码,该编译器将不合格的char
作为unsigned
。在我的系统上,它被视为signed
,我得到-16
。如果你真的想要2的补码char
,那就是signed
,那么你可以这样写:
#include <stdio.h>
int main(void)
{
signed char i = 0xFF, j = 0xF0;
printf("%d\n", ((i<<8) | j));
return 0;
}
仅供参考,附录J.3.4实现定义的行为字符
signed char
或unsigned char
中的哪一个具有相同的范围,表示, 和行为为''普通''char
(6.2.5,6.3.1.1)。
在J.3.5实现定义的行为中整数
是否使用符号和幅度表示有符号整数类型,两个 补充,或补充,以及非凡的价值是否是一个陷阱 代表性或普通价值(6.2.6.2)。
正如Maciej正确指出的那样,应该注意的是,负值的左移是未定义的行为,因此应该避免,因为编译器可能会认为你永远不会将负值移到左边。
6.5.7按位移位运算符ad 4
E1 << E2
的结果是E1
左移E2
位位置;腾出的位用零填充。如果E1
具有unsigned
类型,则结果的值为E1 × 2^E2
,比结果类型中可表示的最大值减少一个模数。如果E1
具有签名类型且非负值,并且结果类型中可以表示E1 × 2^E2
,那么这就是结果值; 否则,行为未定义。
答案 3 :(得分:1)
通常,C / C ++中有符号值的位操作可能具有未定义的结果(未指定数字的特定格式 - 关于移位的部分中的特定措辞) - 有关详细信息,请参阅C99标准。虽然大多数体系结构目前使用2s补码,并且大多数编译器都会生成正确的代码,依靠这样的假设是不明智的 - compilers are known to introduce new optimalizations, which break incorrect code,即使所述代码具有“微不足道”的含义(对于人类而言)。
unsigned char i = 0xFF; // Char might be either signed or unsigned by default
unsigned char j = 0xF0;
uint16_t bit_result = (i << 8) | j; // 0XFFF0
int32_t sign = (bit_result & (1U << 15)) ? -(1U << 15) : 0;
int32_t result = sign + (bit_result & ((1U << 15) - 1));
上面的代码在优化后没有跳转[阻止i和j的不断传播所以它应该几乎和下面的代码一样快:
// WARNING: Undefined behaviour. Might return wrong value (depending on compiler, processor etc.)
unsigned char i = 0xFF;
unsigned char j = 0xF0;
unsigned uint16_t bit_result = (i << 8) | j; // 0xFFF0
int16_t result = bit_result;
如果这是性能关键代码并且第二个代码更快,您可能会考虑第二个代码。其他明智的我会使用第一个更正确。