如何便携地找出 min(INT_MAX, abs(INT_MIN))?

How to portably find out min(INT_MAX, abs(INT_MIN))?

如何方便地找出 INT_MAX 和 abs(INT_MIN) 中的最小值? (这是 INT_MIN 的数学绝对值,而不是对 abs 函数的调用。)

它应该与大多数系统中的 INT_MAX 相同,但我正在寻找一种更便携的方式。

在大多数系统上,没有定义 abs (INT_MIN)。例如,在典型的 32 位机器上,INT_MAX = 2^31 - 1,INT_MIN = - 2^31,而 abs (INT_MIN) 不能是 2^31。

abs(INT_MIN) 将调用未定义的行为。标准说

7.22.6.1 abslabsllabs 函数:

The abs, labs, and llabs functions compute the absolute value of an integer j. If the result cannot be represented, the behavior is undefined.

试试这个:
INT_MIN 转换为 unsignrd int。由于 -ve 数字不能表示为 unsigned intINT_MAX 将转换为 UINT_MAX + 1 + INT_MIN.

#include <stdio.h>
#include <stdlib.h>

unsigned min(unsigned a, unsigned b)
{
    return a < b ? a : b;
}

int main(void)
{
    printf("%u\n", min(INT_MAX, INT_MIN));
}  
INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN

编辑补充说明: 当然难点是如果-INT_MIN太大了,-INT_MINabs(INT_MIN)将不确定适合 int。所以我们需要一些方法来检查是否是这种情况。条件 INT_MAX + INT_MIN < 0 测试 -INT_MIN 是否大于 INT_MAX。如果是,则 INT_MAX 是两个绝对值中较小的一个。如果不是,则INT_MAX是两个绝对值中较大的一个,-INT_MIN是正确答案。

据我所知,

-INT_MAX 在所有 C 和 C++ 方言中都可以表示为 int。因此:

-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX

在 C99 及更高版本中,INT_MAX

引用规范:

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:

  • the corresponding value with sign bit 0 is negated (sign and magnitude);
  • the sign bit has the value −(2^M) (two’s complement);
  • the sign bit has the value −(2^M − 1) (ones’ complement).

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 的第 6.2.6.2 节)

INT_MIN典型值为-2147483648,而INT_MAX典型值为2147483647,标准不保。 TL;DR:您要搜索的值是 INT_MAX 在符合标准的实现中。但是 计算 min(INT_MAX, abs(INT_MIN)) 不可移植。


INT_MININT_MAX的可能值

INT_MININT_MAX由Annex E (Implementation limits) 1定义(C标准,C++继承了这个东东):

The contents of the header are given below, in alphabetical order. The minimum magnitudes shown shall be replaced by implementation-defined magnitudes with the same sign. The values shall all be constant expressions suitable for use in #if preprocessing directives. The components are described further in 5.2.4.2.1.

[...]

#define INT_MAX +32767

#define INT_MIN -32767

[...]

标准要求类型 int 是可以表示范围 [INT_MIN, INT_MAX] 的整数类型(第 5.2.4.2.1. 节)。

然后,6.2.6.2。 (整数类型,同样是 C 标准的一部分),发挥作用并将其进一步限制为我们所知道的 two's or ones' complement:

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:

— the corresponding value with sign bit 0 is negated (sign and magnitude);

— the sign bit has the value −(2M) (two’s complement);

— the sign bit has the value −(2M − 1) (ones’ complement).

第 6.2.6.2 节。将有符号整数类型的值表示与其无符号兄弟的值表示相关联也非常重要。

这意味着,您得到范围 [-(2^n - 1), (2^n - 1)][-2^n, (2^n - 1)],其中 n 通常 15 或 31。

有符号整数类型的运算

现在第二件事:对有符号整数类型的操作,导致值不在 [INT_MIN, INT_MAX] 范围内,行为未定义。这在 C++ 中由第 5/4 段明确规定:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

对于 C,6.5/5 提供了一个非常相似的段落:

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

那么,如果 INT_MIN 的值恰好小于 INT_MAX 的负数(例如分别为 -32768 和 32767),会发生什么情况?计算-(INT_MIN)会是undefined,等同于INT_MAX + 1.

因此我们需要避免计算可能不在 [INT_MIN, INT_MAX] 范围内的值。幸运的是,INT_MAX + INT_MIN 始终在该范围内,因为 INT_MAX 是一个严格的正值,而 INT_MIN 是一个严格的负值。因此 INT_MIN < INT_MAX + INT_MIN < INT_MAX.

现在我们可以检查 INT_MAX + INT_MIN 是否等于、小于或大于 0。

`INT_MAX + INT_MIN`  |  value of -INT_MIN    | value of -INT_MAX 
------------------------------------------------------------------
         < 0         |  undefined            | -INT_MAX
         = 0         |  INT_MAX = -INT_MIN   | -INT_MAX = INT_MIN
         > 0         |  cannot occur according to 6.2.6.2. of the C standard

因此,要确定INT_MAX-INT_MIN中的最小值(在数学意义上),下面的代码就足够了:

if ( INT_MAX + INT_MIN == 0 )
{
    return INT_MAX; // or -INT_MIN, it doesn't matter
}
else if ( INT_MAX + INT_MIN < 0 )
{
    return INT_MAX; // INT_MAX is smaller, -INT_MIN cannot be represented.
}
else // ( INT_MAX + INT_MIN > 0 )
{
    return -INT_MIN; // -INT_MIN is actually smaller than INT_MAX, may not occur in a conforming implementation.
}

或者,为了简化:

return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;

三元运算符中的值只会在必要时进行评估。因此,-INT_MIN 要么未被评估(因此不能产生 UB),要么是一个明确定义的值。

或者,如果你想要一个断言:

assert(INT_MAX + INT_MIN <= 0);
return INT_MAX;

或者,如果您希望在编译时这样做:

static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation");
return INT_MAX;

正确进行整数运算(即如果正确性很重要)

如果您对安全整数运算感兴趣,请查看我的 implementation of safe integer operations. If you want to see the patterns (rather than this lengthy text output) on which operations fail and which succeed, choose this demo

根据架构的不同,可能还有其他选项来保证正确性,比如gcc的选项-ftrapv.