是否有一种可移植的方式来定义 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 &lt;&lt; (sizeof(int)*CHAR_BIT-1) 可能“有效”,但根据 C17dr § 6.5.7 4 是 UB【参考方案3】:

不,因为没有可移植的方法来知道intunsigned 之间值位数的差异。

有一种可移植的方式来获取UINT_MAX,即-1u,因为无符号整数是模类型。因此,INT_MAX 的表达式为

(int)(UINT_MAX >> (value_bits<unsigned> - value_bits<int>))

很遗憾,没有办法得到value_bits&lt;unsigned&gt; - value_bits&lt;int&gt;

在 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 的补码表示。

【讨论】:

允许有符号类型中的符号位是对应无符号类型中的填充位:intunsigned 可以有相同数量的值位,在这种情况下@ 987654327@ 和 INT_MAX 将相等。

以上是关于是否有一种可移植的方式来定义 INT_MAX?的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?

标准 C++ 中是不是有一种可移植的方式来检索主机名?

是否有一种可移植的(C++ 标准)方法来计算前一个对齐的指针?

有没有一种可移植的方式来用 Qt 给线程名称?

有没有一种可移植的方法来设置flock() 的超时时间?

使用 testthat 在单元测试中重用变量。是不是有一种可接受的做法来避免重新创建计算量大的变量