是否有一种可移植的方式来定义 INT_MAX?
Posted
技术标签:
【中文标题】是否有一种可移植的方式来定义 INT_MAX?【英文标题】:Is there a portable way to define INT_MAX? 【发布时间】:2013-07-23 02:57:54 【问题描述】:我在 /usr/include/limits.h 中找到了以下定义:
# define INT_MIN (-INT_MAX - 1)
# define INT_MAX 2147483647
此外,这个头文件中的所有 XXX_MAX 似乎都是从一个数字常量显式定义的。
我想知道是否有一种可移植的方式(针对跨平台的不同字长)来定义 INT_MAX ?
我尝试了以下方法:
~((int)-1)
但这似乎不正确。
简短的解释也受到高度重视。
【问题讨论】:
~((int)-1)
可能为零。
哦,是的!你说的对。但我不明白为什么。你能解释一下吗?非常感谢!
-1
的位模式在二进制补码系统中都是 1。全一字的补码 (~
) 是全零字。
太棒了!谢谢! @CarlNorum
你为什么关心,因为<limits.h>
是在 C99 & C11 & POSIX 标准中指定的?
【参考方案1】:
对于标准头文件limits.h
中的INT_MAX
,实现者的双手被这样一个事实束缚住了:它需要在预处理器#if
指令中可用。这排除了任何涉及 sizeof
或演员表的事情。
如果您只想要一个在实际 C 表达式中工作的版本,也许这会工作:
(int)-1U/2 == (int)(-1U/2) ? (int)-1U : (int)(-1U/2)
这里的概念是int
可能有与unsigned
相同数量的值位,或者少一个值位; C标准允许。为了测试它是什么,检查转换结果(int)-1U
。如果-1U
适合int
,则其值必须通过强制转换保持不变,因此相等将成立。如果-1U
不适合int
,则强制转换会产生int
类型的实现定义结果。但是,无论值是什么,仅根据可能值的范围,相等性将是错误的。
请注意,从技术上讲,转换为int
可能会导致引发实现定义的信号,而不是获得实现定义的值,但是当您处理常量时不会发生这种情况将在编译时评估的表达式。
【讨论】:
我没有看到 C 标准排除无符号类型的值位比相应的有符号类型多 1 个以上,在这种情况下,(int)-1U/2
和 (int)(-1U/2)
都可能导致实现定义的值,可能是同一个。
“预处理器”部分确实令人印象深刻且很有帮助,表达式也是如此。非常感谢!
@caf:确实。可以通过将 2 替换为更大的值(例如 -1U/2+1
)来修复测试,但是我没有针对 else 表达式的好方法。当然,您可以将一堆这些链接到您希望看到的位数的合理上限......
还要提到预处理器计算[u]intmax_t
中的所有整数值,这样像这里给出的表达式这样的任何技巧也不起作用,因为当时使用的常量不是正确的类型.
(int)(-1U / 2) != -1U / 2
发现无符号中的值位比有符号的多 1 个以上的情况,但我仍然无法提出 INT_MAX
表达式。【参考方案2】:
我喜欢这些定义:
#define INT_MIN (1 << (sizeof(int)*CHAR_BIT-1))
#define INT_MAX (-(INT_MIN+1))
【讨论】:
1 << (sizeof(int)*CHAR_BIT-1)
可能“有效”,但根据 C17dr § 6.5.7 4 是 UB【参考方案3】:
不,因为没有可移植的方法来知道int
和unsigned
之间值位数的差异。
有一种可移植的方式来获取UINT_MAX
,即-1u
,因为无符号整数是模类型。因此,INT_MAX
的表达式为
(int)(UINT_MAX >> (value_bits<unsigned> - value_bits<int>))
很遗憾,没有办法得到value_bits<unsigned> - value_bits<int>
。
在 C++ 中,这似乎可以通过模板元编程实现,从 15 位开始递归。 (范围 [-32767, 32767] 保证可以在 int
中表示。)
template<int bits>
struct max0
static const int value = max0<bits - 1>::value * 2 + 1;
;
template<>
struct max0<15>
static const int value = 32767;
;
template<int bits>
struct max1
static const int value =
max0<bits + 1>::value > max0<bits>::value ?
max1<bits + 1>::value :
max0<bits>::value;
;
#define INT_MAX (max1<15>::value)
但我觉得这有点矫枉过正,并坚持使用编译器定义的 __INT_MAX__
;(
编辑:糟糕!
max.cpp: In instantiation of 'const int max0<32>::value':
max.cpp:17:51: recursively required from 'const int max1<16>::value'
max.cpp:17:51: required from 'const int max1<15>::value'
max.cpp:28:28: required from here
max.cpp:4:52: warning: integer overflow in expression [-Woverflow]
static const int value = max0<bits - 1>::value * 2 + 1;
~~~~~~~~~~~~~~~~~~~~~~^~~
max.cpp:4:22: error: overflow in constant expression [-fpermissive]
static const int value = max0<bits - 1>::value * 2 + 1;
^~~~~
max.cpp:4:22: error: overflow in constant expression [-fpermissive]
max.cpp:4:22: error: overflow in constant expression [-fpermissive]
max.cpp: In instantiation of 'const int max0<914>::value':
max.cpp:17:51: recursively required from 'const int max1<16>::value'
max.cpp:17:51: required from 'const int max1<15>::value'
max.cpp:28:28: required from here
max.cpp:4:52: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
static const int value = max0<bits - 1>::value * 2 + 1;
~~~~~~~~~~~~~~~~~~~~~~^~~
compilation terminated.
【讨论】:
【参考方案4】:如果我们假设 1 或 2 的补码表示法和 8 位/字节且没有填充:
#define INT_MAX ((1 << (sizeof(int)*8 - 2)) - 1 + (1 << (sizeof(int)*8 - 2)))
我没有看到班次或加班有任何溢出。我也没有看到UB。我想可以使用CHAR_BIT
而不是 8。
在 1 和 2 的恭维中,最大 int 将是 power(2,(sizeof(int)*Bits_per_byte - 1) - 1
。我们将使用 shift 代替 power,但我们不能一次移动 太多。所以我们通过两次做一半来形成power(2,(sizeof(int)*Bits_per_byte - 1)
。但是溢出是一个禁忌,所以在添加第二半之前减去 1 而不是在末尾。我使用了很多()
来强调评估顺序。
正如@caf 所指出的,这种方法失败是有填充位 - 不常见但可能。
计算 INT_MIN 在 2 的 和 1 的恭维中工作有点棘手,但类似的方法也可以,但这是另一个问题。
【讨论】:
即使你使用CHAR_BIT
,这也没有考虑到填充位的可能性,这是不常见但允许的。
@caf 我确定你是对的。你知道确定填充位数的方法或定义吗?
不常见的填充位的潜在计数和位置可能在理论上排除了制定通用 INT_MAX 公式。
我认为不冒未定义行为的风险是不可能的。
@chux:可以在编译时检测到填充的 int。如果int
有任何填充位,则struct signed int foo:(sizeof(int)*CHAR_BIT-1); ;
是违反约束的。【参考方案5】:
嗯,可以试试
#define INT_MAX (int) ((unsigned) -1 / 2)
哪个“应该”跨平台工作,字长不同,甚至有符号整数值的不同表示。 (unsigned) -1
将可移植地生成 UINT_MAX
值,这是全比特模式。将其除以 2 应为对应的有符号整数类型的预期最大值,该值用于表示符号的位。
但是为什么呢?标准头文件和其中的定义不应该是可移植的。
顺便说一句,您上面引用的INT_MIN
的定义不可移植。它特定于有符号整数的 2 的补码表示。
【讨论】:
允许有符号类型中的符号位是对应无符号类型中的填充位:int
和unsigned
可以有相同数量的值位,在这种情况下@ 987654327@ 和 INT_MAX
将相等。以上是关于是否有一种可移植的方式来定义 INT_MAX?的主要内容,如果未能解决你的问题,请参考以下文章
是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?