为什么numeric_limits<uint16_t>::max()不等于-1?

7
#include <iostream>
#include <cstdint>

using namespace std;

static_assert(-1 == numeric_limits<uint64_t>::max()); // ok
static_assert(-1 == numeric_limits<uint32_t>::max()); // ok
static_assert(-1 == numeric_limits<uint16_t>::max()); // error

int main()
{
    cout << numeric_limits<uint16_t>::max() << endl;
    cout << uint16_t(-1) << endl;
}

输出:

65535
65535

为什么 numeric_limits<uint16_t>::max() 不等于 -1?

更新:

根据cppref

同样,USHRT_MAX可能不是无符号类型:它的类型可能是int。


2
"may be int" 和 "is int" 是两个非常不同的概念。 - Asteroids With Wings
你能否更详细地解释一下为什么你认为它应该等于-1呢?如果我们不知道误解在哪里,那么澄清起来就很困难! - Asteroids With Wings
对我来说,-1 的意思是 0xFFFF....,所以对于无符号整数,它应该等于该整数类型的最大值。 - xmllmx
好的。-1并不意味着0xFFFF。它代表的是-1。听起来你把C++当作内存中的原始编程位来处理。但实际上不是这样的。 - Asteroids With Wings
即使它确实按照那种方式工作,在现代系统上,它更可能表示为0xFFFFFFFF。你是假设int像20世纪80年代一样是16位吗? - Asteroids With Wings
1
请注意我的回答中的补充说明,涉及特定平台/依赖行为。 - dfrib
2个回答

13

整数转换和提升

uint16_t值经过整数提升,而对于(您特定的平台;请参见下文)uint32_tuint64_t情况,-1 (不是一个整数字面量,而是应用于整数字面量1的一元减号运算符)经过整数转换,其结果值等于uint32_tuint64_t类型的最大相应值,由于此转换的源值和目标值之间的整数同余关系。

static_assert(-1 == std::numeric_limits<std::uint64_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint64_t
//            |   Resulting value: std::numeric_limits<std::uint64_t>::max()

static_assert(-1 == std::numeric_limits<std::uint32_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint32_t
//            |   Resulting value: std::numeric_limits<std::uint32_t>::max()

static_assert(-1 == std::numeric_limits<std::uint16_t>::max());
//                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                  | Integer promotion:
//                  |   Destination type: int
//                  |   Resulting value: std::numeric_limits<std::uint16_t>::max()

[expr.eq]/1[expr.eq]/6中(强调部分为本人添加):

[expr.eq]/1

==(等于)和!=(不等于)运算符从左到右进行分组。操作数必须具有算术、枚举、指针或成员指针类型,或类型std::nullptr_­t。运算符==!=都产生truefalse的结果,即bool类型的结果。在下面的每种情况下,在应用指定的转换后,操作数必须具有相同的类型

[expr.eq]/6

如果两个操作数都是算术**或枚举类型,则对两个操作数执行通常的算术转换;如果指定的关系是true,则每个运算符都应产生true,否则应产生false

[conv.integral]/1[conv.integral]/2得知:

[conv.integral]/1

整数类型的prvalue可以转换为另一种整数类型的prvalue。未作用域的枚举类型的prvalue可以转换为整数类型的prvalue。

[conv.integral]/2

如果目标类型是无符号的,则结果值是与源整数同余的最小无符号整数(模2n,其中n是用于表示无符号类型的位数)。[注意:在二进制补码表示中,此转换是概念性的,如果没有截断,位模式不会发生变化。—结尾注意]

仅凭这些信息应该能够产生您三个示例的相同行为。然而,对于uint16_t情况,[conv.integral]/5适用:

[conv.integral]/5

整型提升允许的转换不包括在整型转换的集合中。

来自[conv.rank]/1

[conv.rank]/1

每个整数类型都有一个整数转换等级,定义如下:

[...]

(1.3) long long int 的等级应大于 long int 的等级,int 的等级应大于 short int 的等级short int 的等级应大于 signed char 的等级。

(1.4) 任何无符号整数类型的等级应等于相应有符号整数类型的等级。

uint16_t 的整数转换等级(与 short int 相同或更低)低于 int 的等级,这意味着 [conv.prom]/1 适用于 uint16_t [强调我的]:

[conv.prom]/1

除了 boolchar16_­tchar32_­twchar_­t 的整数类型的 prvalue,如果其整数转换等级低于 int 的等级,则可以将其转换为类型为 int 的 prvalue,如果 int 可以表示源类型的所有值;否则,源 prvalue 可以被转换为类型为 unsigned int 的 prvalue。


平台相关行为

然而,虽然我们能够因为无符号短整型的最大值下限要求来证明uint16_t的优先级总是低于int的整数转换等级,但我们不能为uint32_t提出相反的论点,因为ISO C++标准对基本整数类型的最大值没有上限要求。

[basic.fundamental]/2[basic.fundamental]/3中提取(强调属于我):

[basic.fundamental]/2

有五种标准的带符号整数类型:“signed char”、“short int”、“int”、“long int”和“long long int”。在此列表中,每个类型提供的存储空间至少与它前面的类型相同。[...] 普通的int具有由执行环境的体系结构建议的自然大小;其他带符号整数类型是为了满足特殊需要而提供的。

[basic.fundamental]/3

对于每个标准带符号整数类型,都存在一个对应的(但不同的)标准无符号整数类型:“unsigned char”、“unsigned short int”、“unsigned int”、“unsigned long int”和“unsigned long long int”,每个类型占用与相应的带符号整数类型相同的存储空间并具有相同的对齐要求;[...]

带符号和无符号整数类型必须满足C标准第5.2.4.2.1节中给出的约束条件

而且,从C11标准草案中提取(强调属于我):

5.2.4.2.1 整数类型的大小<limits.h> [...] 它们的实现定义值应该与所示的相等或更大(绝对值),符号相同。
[...]
- 类型为short int的对象的最大值: SHRT_MAX +32767 - 类型为int的对象的最大值: INT_MAX +32767 [...]
请注意,这些最大值描述了各自基本整数类型应该能够存储的最大值的下限,而对于这些最大值的上限没有要求。此外,请回想一下上面的[basic.fundamental]/2引用,每个后续基本(有符号)整数类型只需要提供至少与前面的类型相同的存储空间(在列表中)。
这意味着,理论上,平台可以将short intint分别实现为32位宽度和64位宽度的整数,这意味着在此平台上,uint32_t将具有与(unsigned)short int相同的整数转换级别,这将意味着比int更低的转换级别,如果是这种情况,则[conv.prom]/1也适用于uint32_t示例在此特定平台上

8

因为-1没有被转换成uint16_t

std::numeric_limits<std::uint16_t>::max()被提升为一个int,而-1 != 65535


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接