【问题标题】:Correct way to take absolute value of INT_MIN获取 INT_MIN 绝对值的正确方法
【发布时间】:2012-09-01 21:29:52
【问题描述】:

我想在无符号中执行一些算术,并且需要取负 int 的绝对值,比如

do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
   unsigned int magnitude;
   int negative;
   if(some_signed_value<0) {
       magnitude = 0 - some_signed_value;
       negative = 1;
    } else {
       magnitude = some_signed_value;
       negative = 0;
    }
   ...snip...
}

但是 INT_MIN 可能有问题,如果在有符号算术中执行,0 - INT_MIN 是 UB。 在 C 中执行此操作的标准/稳健/安全/高效的方法是什么?

编辑:

如果我们知道我们在 2-complement 中,那么隐式转换和显式位操作可能是标准的吗?如果可能的话,我想避免这种假设。

do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
   unsigned int magnitude=some_signed_value;
   int negative=some_signed_value<0;
   if (negative) {
       magnitude = (~magnitude) + 1;
    }
   ...snip...
}

【问题讨论】:

    标签: c


    【解决方案1】:

    从有符号到无符号的转换是明确定义的:你得到对应的代表模 2N。因此,以下将为您提供n 的正确绝对值:

    int n = /* ... */;
    
    unsigned int abs_n = n < 0 ? UINT_MAX - ((unsigned int)(n)) + 1U
                               : (unsigned int)(n);
    

    更新:正如@aka.nice 建议的那样,我们实际上可以将UINT_MAX + 1U 替换为0U

    unsigned int abs_n = n < 0 ? -((unsigned int)(n))
                               : +((unsigned int)(n));
    

    【讨论】:

    • 哦,在UINT_MAX == INT_MAX == -(INT_MIN+1) 的纯理论情况下,无论如何都不可能将|INT_MIN| 表示为unsigned int =)
    • @DanielFischer:考虑到intunsigned int 需要具有相同的大小和对齐要求,这种情况真的可行吗?
    • 有可能unsigned int 可以比int 多一个填充位。我从未听说过这种情况的实现,但标准并不能保证它永远不会发生。 (除非我忽略了什么。)
    • 如果我写 abs_n = n
    • 其实我提议的测试是错误的。它考虑了 2 的补码实现,但不考虑 1 的补码或符号幅度,即使绝对值不合适,(unsigned)INT_MIN != 0 也会为真。如果您希望函数返回 unsigned int,“Fischer 条件”是必要且充分的,但对其进行正确测试并不是那么简单......
    【解决方案2】:

    在否定的情况下,取some_signed_value+1。否定它(这是安全的,因为它不能是INT_MIN)。转换为无符号。然后加一个;

    【讨论】:

    • 我检查并 gcc 为 1U+(unsigned)(-(x+1)) 和 -(unsigned)(x) 生成相同的代码,类似于 (~magnitude)+1 但无分支,所以两者都将同样有效。不过,后来的意图似乎不那么模糊了。
    • @aka.nice:是的,我也刚查过。 1+(unsigned)-(x+1) 可能有点晦涩难懂,但它并没有引入带负符号量到无符号量的值转换;演员表纯粹是类型的变化,而不是价值的变化。在您的版本中,需要进行一些推理工作以确保算术符合预期;论据并不像“每一步的值都在安全范围内”那么简单。
    • 是的,您的解决方案确实更符合我的初衷。将负数 x 重新解释为正数对于仔细阅读代码的人来说是一种模糊的意图,并且需要了解标准约定。但不太细心的读者会立即认出 -(unsigned)x... 中的 abs 形式...
    • 建议添加代码这个很好的答案:类似unsigned abs_u(int i) { return i &lt; 0 ? -(i+1) + 1u : (unsigned) i; }
    【解决方案3】:

    您始终可以测试&gt;= -INT_MAX,这始终是明确定义的。对您来说唯一有趣的情况是INT_MIN &lt; -INT_MAXsome_signed_value == INT_MIN。您必须单独测试该案例。

    【讨论】:

      【解决方案4】:

      我想在unsigned做一些算术运算,需要取负的绝对值int,...

      处理迂腐案件:

      |SOME_INT_MIN|1 有一些特殊情况:

      1.非二的补码

      现在很少看到一个人的补码和符号大小。

      SOME_INT_MIN == -SOME_INT_MAXsome_abs(some_int) 定义明确。这是简单的情况。

      #if INT_MIN == -INT_MAX
      some_abs(x);  // use matching abs, labs, llabs, imaxabs
      #endif
      

      2。 SOME_INT_MAX == SOME_UINT_MAX,2 的补码

      C 允许整数类型的 signedunsigned 版本的最大值相同。这几天很少见。

      2 种方法:
      1) 使用更广泛的整数类型(如果存在)。

      #if -INTMAX_MAX <= SOME_INT_MIN
      imaxabs((intmax_t)x)
      #endif
      

      2) 使用宽(st)浮点(FP)类型。
      转换为宽 FP 将适用于 SOME_INT_MIN(2 的补码),因为该值为 -(power-of-2)。对于其他较大的负数,转换可能会丢失宽整数而不是那么宽 long double 的精度。例如。 64 位 long long 和 64 位 long double

      fabsl(x);  // see restriction above.
      

      3. SOME_INT_MAX &lt; SOME_UINT_MAX

      这是@Kerrek SB 回答的常见情况。下面也处理案例1。

      x < 0 ? -((unsigned) x) : ((unsigned) x);
      

      更高级别的替代方案

      在代码执行.... + abs(x) 的情况下,一个明确定义的替代方法是减去负绝对值:.... - nabs(x)。或者如abs(x) &lt; 100,使用nabs &gt; -100

      // This is always well defined.
      int nabs(int x) {
        return (x < 0) x : -x;
      }
      

      1SOME_INT 表示intlonglong longintmax_t

      【讨论】:

        【解决方案5】:
         static unsigned absolute(int x)
         {
                  if (INT_MIN == x) {
                          /* Avoid tricky arithmetic overflow possibilities */
                          return ((unsigned) -(INT_MIN + 1)) + 1U;
                  } else if (x < 0) {
                          return -x;
                  } else {
                          return x;
                  }
         }
        

        【讨论】:

        • 听起来不错,但主要是@R 答案加上一个分支,或者只是@Jens 答案...
        猜你喜欢
        • 1970-01-01
        • 2012-01-21
        • 1970-01-01
        • 1970-01-01
        • 2013-06-02
        • 2015-03-02
        • 2015-04-08
        • 1970-01-01
        • 2017-11-19
        相关资源
        最近更新 更多