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

xiv*_*r77 30 c c++ math limits

我怎样才能找到最小的INT_MAX和abs(INT_MIN)?(这是数学的绝对值INT_MIN,而不是对abs函数的调用.)

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

ste*_*fan 23

虽然典型值为INT_MIN-2147483648,典型值为INT_MAX2147483647,但标准不保证.TL; DR:您要搜索的值是INT_MAX符合要求的实现.但计算 min(INT_MAX, abs(INT_MIN))不便携.


的可能值INT_MININT_MAX

INT_MIN并且INT_MAX由附件E(实施限制)1(C标准,C++继承这个东西)之一定义:

标题的内容按字母顺序给出如下.显示的最小幅度应由具有相同符号的实现定义的幅度替换.这些值都应该是适用于#if预处理指令的常量表达式.这些组件在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标准的一部分)发挥作用,并进一步将其限制为我们所知的两个或一个补码:

对于有符号整数类型,对象表示的位应分为三组:值位,填充位和符号位.不需要任何填充位; signed char不应有任何填充位.应该只有一个符号位.作为值位的每个位应具有与相应无符号类型的对象表示中的相同位相同的值(如果有符号类型中有M个值位且无符号类型中有N,则M≤N).如果符号位为零,则不应影响结果值.如果符号位为1,则应以下列方式之一修改该值:

- 符号位0的相应值被否定(符号和幅度);

- 符号位的值为 - (2M)(二进制补码);

- 符号位的值为 - (2M - 1)(补码).

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

这意味着,你要么范围[-(2^n - 1), (2^n - 1)][-2^n, (2^n - 1)],其中n典型的 15或31.

有符号整数类型的操作

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

如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义.

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

如果在计算表达式期间发生异常情况(即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义.

那么如果值INT_MIN恰好小于负值INT_MAX(例如分别为-32768和32767)会发生什么?计算-(INT_MIN)将是未定义的,与之相同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
Run Code Online (Sandbox Code Playgroud)

因此,要确定最小值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.
}
Run Code Online (Sandbox Code Playgroud)

或者,为了简化:

return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;
Run Code Online (Sandbox Code Playgroud)

只有在必要时才会评估三元运算符中的值.因此,-INT_MIN要么保持未评估(因此不能产生UB),要么是定义明确的值.

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

assert(INT_MAX + INT_MIN <= 0);
return INT_MAX;
Run Code Online (Sandbox Code Playgroud)

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

static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation");
return INT_MAX;
Run Code Online (Sandbox Code Playgroud)

正确地进行整数运算(即正确性是否重要)

如果您对安全整数运算感兴趣,请查看我的安全整数运算实现.如果要查看哪些操作失败并且成功的模式(而不是这个冗长的文本输出),请选择此演示.

根据体系结构的不同,可能还有其他选项可以确保正确性,例如gcc的选项-ftrapv.


Bri*_*ian 10

INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN
Run Code Online (Sandbox Code Playgroud)

编辑添加解释:当然,如果太大而不适合,那么难以定义-INT_MINabs(INT_MIN)将是未定义-INT_MINint.所以我们需要一些方法来检查是否是这种情况.条件INT_MAX + INT_MIN < 0测试是否-INT_MIN大于INT_MAX.如果是,则INT_MAX是两个绝对值中较小的一个.如果不是,则INT_MAX两个绝对值中较大的一个-INT_MIN是正确的答案.

  • @kdopen然后不计算带有`-INT_MIN`的分支,这意味着UB不会发生. (7认同)
  • @JensGustedt这是一个类似的假设,`-INT_MAX`可以表示为`int`. (3认同)
  • 实际上,它仍然不正确,因为`-INT_MIN`不能在`int`中表示 (2认同)

Jer*_*man 7

在C99及以上,INT_MAX.

规范:

对于有符号整数类型,对象表示的位应分为三组:值位,填充位和符号位.不需要任何填充位; signed char不应有任何填充位.应该只有一个符号位.作为值位的每个位应具有与相应无符号类型的对象表示中的相同位相同的值(如果有符号类型中有M个值位且无符号类型中有N,则M≤N).如果符号位为零,则不应影响结果值.如果符号位为1,则应以下列方式之一修改该值:

  • 符号位0的相应值被否定(符号和幅度);
  • 符号位的值为 - (2 ^ M)(二进制补码);
  • 符号位的值为 - (2 ^ M - 1)(1'补码).

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

  • haccks:在C99及以上版本中,表示限制可防止abs(INT_MIN)小于INT_MAX. (4认同)
  • haccks:如果abs(INT_MIN)> = INT_MAX(您已同意),则min(INT_MAX,abs(INT_MIN))== INT_MAX. (2认同)