c++ - 为什么是 numeric_limits::max() 不等于 -1?
问题描述
#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。
解决方案
整数转换和促销
该uint16_t
值经过整数提升uint32_t
,而对于(您的特定平台;见下文)uint64_t
该-1
值(本身不是整数文字,而是应用于整数文字的一元减号运算符1
)经过整数转换并得到结果值由于此转换的源值和目标值之间的整数一致性,这等于uint32_t
和类型的各自的最大值。uint64_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 [强调我的]:
[表达式.eq]/1
(
==
等于)和!=
(不等于)运算符从左到右分组。操作数应具有算术、枚举、指针或指向成员类型或类型的指针std::nullptr_t
。运算符==
and!=
都产生true
orfalse
,即类型 的结果bool
。在以下每种情况下,在应用了指定的转换后,操作数应具有相同的类型。[表达式.eq]/6
如果两个操作数都是算术**或枚举类型,则对两个操作数执行通常的算术转换;
true
如果指定的关系是true
并且false
如果是 ,则每个运算符都应让步false
。
从[conv.integral]/1和[conv.integral]/2:
[conv.integral]/1
整数类型的纯右值可以转换为另一种整数类型的纯右值。无作用域枚举类型的纯右值可以转换为整数类型的纯右值。
[conv.integral]/2
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模
2n
其中n
是用于表示无符号类型的位数)。[ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。——尾注]
仅此一项就应该为所有三个示例产生相同的行为。但是,这种uint16_t
情况的不同之处在于[conv.integral]/5适用:
[conv.integral]/5
作为积分促销允许的转换不包括在积分转换集中。
[转化排名]/1
每个整数类型都有一个整数转换等级,定义如下:
[...]
(1.3) 的等级
long long int
应大于 的等级, 的等级应大于long int
的等级int
, 的等级应大于 的等级, 的等级short int
应大于 的等级signed char
。(1.4) 任何无符号整数类型的秩应等于相应有符号整数类型的秩。
uint16_t
(相同或低于short int
)的整数转换秩低于 的int
,这意味着[conv.prom]/1适用于uint16_t
[强调我的]:
[conv.prom]/1
如果可以表示源类型的所有值,则可以将除
bool
,char16_t
,之外的整数类型的纯右值char32_t
,或者wchar_t
整数转换秩小于 的秩的int
纯右值转换为类型的纯右值;int
int
否则,源纯右值可以转换为类型的纯右值unsigned int
。
平台相关行为
然而,虽然我们能够为uint16_t
上述论证提出一个论点,因为对最大值的下限要求——unsigned short int
保证uint16_t
总是具有比整数转换等级更低的——我们不能为永远不会更低int
的事实提出相反的论点整数转换等级高于,因为 ISO C++ 标准对基本整数类型的最大值要求没有上限。uint32_t
int
从[basic.fundamental]/2和[basic.fundamental]/3 [摘录,强调我的]:
[basic.fundamental]/2
有五种标准有符号整数类型:“<code>signed char”、“<code>short int”、“<code>int”、“<code>long int”和“<code>long long int”。在此列表中,每种类型提供的存储空间至少与列表中它前面的类型一样多。[...] Plain具有执行环境架构所建议的自然
int
大小;提供其他有符号整数类型以满足特殊需要。[basic.fundamental]/3
对于每个标准有符号整数类型,都存在相应的(但不同的)标准无符号整数类型:“<code>unsigned char”、“<code>unsigned short int”、“<code>unsigned int”、“<code >unsigned long int”和“<code>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 引用中回想一下,每个后续的基本(有符号)整数类型只需要提供至少与处理它的那个(在列表中)一样多的存储空间。
这意味着,理论上,一个平台可以分别实现 32 位宽和 64 位宽的整数,这意味着在这个平台上,short int
整数转换等级与 ( )相同,这意味着转换等级低于,在这种情况下,也适用于这个特定平台上的示例。int
uint32_t
unsigned
short int
int
[conv.prom]/1
uint32_t
推荐阅读
- python - django API 中的分页
- python - 从源容器复制 blob 并异步上传到目标容器
- reactjs - 如何在“powerbi-client-react”中使用 powerbi 引导和预加载
- python - Pandas Pivot 表有多个值列,但不会按我的顺序放置值列
- html - 在我的样式表中引用元素时遇到问题
- ios - 如何处理嵌套 UITableViews 上的手势识别器?
- python - 使用 Python 和 Youtube API 自动化 Spotify - 错误 403:请求缺少有效的 API 密钥
- c++ - C++| BST 节点指针引用与节点指针
- swift - 无法构建 Objective-C 模块“PryntTrimmerView”
- java - 试图理解为什么嵌套的 switch case 语句没有编译