在C中表示为2的补码

时间:2013-05-06 11:04:02

标签: c twos-complement

我有两个char,我希望我的程序可以作为一个2的补码值。例如,如果我有:

char i = 0xFF;
char j = 0xF0;

int k = ((i<<8) | j);

然后我希望C将k作为2的补码(所以-16代替65520)。我该怎么做?

4 个答案:

答案 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 charunsigned 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;

如果这是性能关键代码并且第二个代码更快,您可能会考虑第二个代码。其他明智的我会使用第一个更正确。