1

我有两个字符,我希望我的程序作为一个 2 的补码值进行交互。例如,如果我有:

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

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

然后我希望 C 将 k 解释为 2 的补码(所以 -16 而不是 65520)。我该怎么做呢?

4

4 回答 4

2

int variables, in comparison to unsigned int are always interpreted as two's complement. Your value is just not -16 :)

after you run your code, k will be (assuming 32 bit integer width)

k == 0x0000FFF0 // k == 65520

whereas:

-16 == 0xFFFFFFF0

what you can do, to overcome this, is setting all bits of k to 1 beforehand

int k = -1;          // k == 0xFFFFFFFF
k &= ((i << 8) | j); // k == 0xFFFFFFF0
于 2013-05-06T11:10:16.213 回答
2

您想将所有最高有效位设置为 1,除了低 16 位。这样的事情应该做到这一点。

k |= (-1&~0xFFFF);

也就是说,如果您的编译器将字符解释为已签名(我认为大多数人都这样做),那么 k 已经是 -16。

此外,对于带符号的字符,如果j设置了其最高有效位(就像在这种情况下那样),您的结果通常是不正确的。在表达式的评估期间,j将被类型提升为负数,并设置所有最高有效位。当这样一个数字与表达式的其余部分进行或运算时,这些位将覆盖其他所有内容。它仅在这种情况下有效,因为已经设置了所有位,因此无论哪种方式都没有区别。

于 2013-05-06T11:07:43.827 回答
1

一般来说,C/C++ 中对有符号值的位操作可能会产生未定义的结果(未指定数字的具体格式 - 有关移位部分的具体措辞) - 详情请参阅 C99 标准。虽然目前大多数架构都使用 2s-complement 并且大多数编译器都会生成正确的代码,但依赖这种假设是不明智的 -众所周知,编译器会引入新的优化,这会破坏不正确的代码,即使所述代码具有“微不足道”的含义(对于人类) .

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;

万一这是性能关键代码并且第二个代码更快,您可能会考虑第二个。否则,我会使用第一个更正确。

于 2013-05-06T11:43:24.783 回答
1

您正在使用带有不合格charas的编译器编译代码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 << E2E1左移的E2位位置;空出的位用零填充。如果E1有一个unsigned类型,则结果的值是E1 × 2^E2,比结果类型中可表示的最大值多一个模减一。如果E1有一个有符号类型和非负值,并且E1 × 2^E2可以在结果类型中表示,那么这就是结果值;否则,行为是 undefined

于 2013-05-06T11:39:18.357 回答