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

时间:2020-04-15 15:04:09

标签: c++ standards numeric unsigned-integer compile-time-constant

#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可能不是无符号类型:其类型可能是

2 个答案:

答案 0 :(得分:6)

因为-1未转换为uint16_t

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

答案 1 :(得分:5)

整数转换和促销

uint16_t值通过整数提升进行,而对于uint32_tuint64_t情况(您的特定平台;请参见下文),-1 value (本身不是整数文字,但是应用于整数文字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 truefalse(如果是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 的等级,该等级应大于 排名signed char

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

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

[conv.prom] / 1

uint16_tboolchar16_­tchar32_­t以外的整数类型的prvalue 其整数转换等级小于{{如果wchar_­t可以代表源类型的所有值,则可以将1}}转换为类型int 的prvalue;否则,可以将源prvalue转换为类型int的prvalue。


依赖平台的行为

然而,尽管我们能够对int进行论证,但由于对{{1}的最大值的下限要求 } –保证unsigned int始终比uint16_t的整数转换排名低-我们不能为unsigned short int永远较低上限。

[basic.fundamental]/2[basic.fundamental]/3中摘录,强调是我的]:

[basic.fundamental] / 2

有五种标准的带符号整数类型:“ uint16_t”, “ int”,“ uint32_t”,“ int”和“ signed char”。在这个 列表中,每种类型提供的存储量至少与之前的存储量相同 在列表中。 [...]普通short int具有建议的自然尺寸 根据执行环境的架构;另一个签名 提供了整数类型以满足特殊需求。

[basic.fundamental] / 3

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

有符号和无符号整数类型应满足约束 C标准第5.2.4.2.1节中给出。

然后,从C11 Standard draft [摘录,重点我的]:

5.2.4.2.1整数类型unsigned short int

的大小

[...]其实现定义的值应等于或更大 大小(绝对值)与所示的相同,并带有相同的符号。

[...]

    类型为unsigned int的对象的
  • 最大值:unsigned long int

  • 类型为unsigned long long int的对象的
  • 最大值:<limits.h>

[...]

请注意,这些最大值描述了相应的基本整数类型应能够存储的最大值的下限,而对这些最大值的上限。此外,从上面的[basic.fundamental] / 2引用中回想起,每个随后的基本(带符号)整数类型仅需要提供至少 个存储空间(与列表中进行存储的存储空间一样大)。

这意味着从理论上讲,平台可以将short intSHRT_MAX +32767分别实现为32位宽和64位宽的整数,这意味着在该平台上,int将具有与({INT_MAX +32767short int相同的整数转换等级,这意味着转换等级比int 低,在这种情况下,将使用uint32_t同样在该特定平台上的unsigned示例