波浪号 C 无符号与有符号整数

Posted

技术标签:

【中文标题】波浪号 C 无符号与有符号整数【英文标题】:Tilde C unsigned vs signed integer 【发布时间】:2012-10-20 07:28:57 【问题描述】:

例如:

unsigned int i = ~0;

结果:我可以分配给i的最大数量

signed int y = ~0;

结果-1

为什么我得到-1?我不应该得到我可以分配给y 的最大数量吗?

【问题讨论】:

您确定您了解 ~ 运算符的作用吗? (按位非) 好吧,-1 是您可以放入整数的 最大 数,但 maximum 定义为绝对二进制值 :) 【参考方案1】:

4294967295(又名UINT_MAX)和-1 都具有0xFFFFFFFF 的相同二进制表示或32 位都设置为1。这是因为有符号数使用two's complement 表示。负数的 MSB(最高有效位)设置为 1,其值通过翻转其余位、加上 1 并乘以 -1 来确定。因此,如果您将 MSB 设置为 1 并且其余位也设置为 1,则翻转它们(获得 32 个零),添加 1(获得 1)并乘以 -1 到终于得到-1

这使得 CPU 更容易进行数学运算,因为它不需要负数的特殊例外。例如,尝试添加0xFFFFFFFF (-1) 和1。由于只有 32 位空间,这将溢出,结果将如预期的那样为0

查看更多:

http://en.wikipedia.org/wiki/Two%27s_complement

【讨论】:

【参考方案2】:
unsigned int i  = ~0;

结果:我可以分配给 i 的最大数量

通常,但不一定。表达式~0 的计算结果为设置了所有(非填充)位的int。 C 标准允许有符号整数的三种表示形式,

二进制补码,在这种情况下 ~0 = -1 并将其分配给 unsigned int 会产生 (-1) + (UINT_MAX + 1) = UINT_MAX。 个数的补码,在这种情况下~0 是负零或陷阱表示;如果它是负零,则分配给 unsigned int 的结果为 0。 符号和幅度,在这种情况下~0INT_MIN == -INT_MAX,并将其分配给unsigned int 会导致(UINT_MAX + 1) - INT_MAX,在不太可能的情况下11 具有宽度(无符号整数类型的值位数,有符号整数类型的值位数 + 1 [符号位])小于int2^(WIDTH - 1) + 1 在常见情况下unsigned int 的宽度为与int的宽度相同。

初始化

unsigned int i = ~0u;

将始终导致 i 持有值 UINT_MAX

signed int y = ~0;

结果:-1

如上所述,只有当有符号整数的表示使用二进制补码时(这是目前最常见的表示)。

【讨论】:

【参考方案3】:

~0 只是一个int,所有位都设置为1。当解释为unsigned 时,这将等效于UINT_MAX。当解释为signed 时,这将是-1

假设 32 位整数:

 0 = 0x00000000 =  0 (signed) = 0 (unsigned)
~0 = 0xffffffff = -1 (signed) = UINT_MAX (unsigned)

【讨论】:

谢谢,但为什么 0xffffffff 是 -1 的签名? ~0 = 0xffffffff = -1(有符号,-1 为 2 的补码形式)。所有系统都遵循这种方法吗? 赋值不是重新解释,而是值转换:当~0 赋值给unsigned int 时,如果~0 类型为int 代表-1,则只会产生UINT_MAX 我认为还值得注意的是,0 - 1 始终是 0xffffffff,如果已签名则解释为 -1,如果未签名则溢出到 UINT_MAX。反之,0xffffffff + 1 始终为0,如果有符号则再次正确,如果无符号则从UINT_MAX 溢出。 @hyde:这是不正确的 - 如果使用二进制补码,0xffffffff 仅表示 -1,而在符号和幅度的情况下表示 -2147483647,在反码的情况下表示 0 (但这可能是一个陷阱表示)【参考方案4】:

保罗的回答是绝对正确的。除了使用 ~0,您可以使用:

#include <limits.h>

signed int y = INT_MAX;
unsigned int x = UINT_MAX;

现在如果你检查值:

printf("x = %u\ny = %d\n", UINT_MAX, INT_MAX);

您可以查看系统上的最大值。

【讨论】:

【参考方案5】:

不,因为~按位非 运算符,而不是类型的最大值 运算符。 ~0 对应于所有位设置为 1int,将其解释为无符号数会为您提供无符号数可表示的最大数,而将其解释为有符号整数则为 -1。

【讨论】:

【参考方案6】:

您必须在two's complement 机器上。

【讨论】:

关于替代系统的简要讨论,以及它今天如何不被使用:en.wikipedia.org/wiki/Ones%27_complement#History C 语言允许二进制补码、二进制补码和符号幅度表示,基于底层硬件,所以它不是完全未使用的。如果有一些基于硬件的因素(速度或成本)来使用其他表示之一,我敢打赌他们会回来。【参考方案7】:

查看http://en.wikipedia.org/wiki/Two%27s_complement,了解一些关于布尔代数和逻辑设计的知识。另外学习如何用二进制计数和用二进制加减法将进一步解释这一点。

C 语言使用这种形式的数字,因此要找到您需要使用的最大数字 0x7FFFFFFF。 (您使用的每个字节使用 2 个 FF,最左边的字节是 7。)要理解这一点,您需要查找十六进制数字及其工作原理。

现在解释无符号等价物。在有符号数中,数字的下半部分为负数(假设 0 为正数,因此负数实际上比正数高 1)。无符号数都是正数。所以理论上你的 32 位 int 的最高数字是 2^32,除了 0 仍然算作正数,所以它实际上是 2^32-1,现在对于有符号数,这些数字的一半是负数。这意味着我们将前一个数字 2^32 除以 2,因为 32 是一个指数,我们在每一边得到 2^31 个数字 0 为正数意味着有符号 32 位 int 的范围是 (-2^31, 2^31- 1).

现在只比较范围: 无符号 32 位整数:(0, 2^32-1) 带符号的 32 位整数:(-2^31, 2^32-1) 无符号 16 位整数:(0, 2^16-1) 带符号的 16 位整数:(-2^15, 2^15-1)

您应该能够在此处看到模式。 要解释 ~0 的事情需要多一点,这与二进制减法有关。它只是加 1 并翻转所有位,然后将两个数字加在一起。 C 在幕后为您执行此操作,许多处理器(包括 x86 和 x64 处理器系列)也是如此。 因此,最好将负数存储为倒数,并且在二进制补码中添加的 1 也被隐藏。因为 0 被假定为正数,所以负数不能有 0 的值,所以它们会自动添加 -1(位翻转后的正 1)。在解码负数时,我们必须考虑到这一点。

【讨论】:

以上是关于波浪号 C 无符号与有符号整数的主要内容,如果未能解决你的问题,请参考以下文章

无符号整型与有符号整型相运算规则

整数溢出漏洞小结

C++ 隐式转换(有符号 + 无符号)

C中的无符号整数在java中的处理

Java不提供无符号整数类型?谢谢

为啥 C 没有无符号浮点数?