有没有一种安全的方法来获得有符号整数的无符号绝对值,而不会触发溢出?

时间:2013-06-26 07:03:53

标签: c++ integer integer-overflow

考虑一个典型的绝对值函数(为了参数起见,最大值的整数类型很长):

unsigned long abs(long input);

这种天真的实现可能类似于:

unsigned long abs(long input)
{
    if (input >= 0)
    {
        // input is positive
        // We know this is safe, because the maximum positive signed
        // integer is always less than the maximum positive unsigned one
        return static_cast<unsigned long>(input);
    }
    else
    {
        return static_cast<unsigned long>(-input); // ut oh...
    }
}

此代码触发未定义的行为,因为input的否定可能会溢出,并且触发有符号整数溢出是未定义的行为。例如,在2s补码机器上,std::numeric_limits<long>::min()的绝对值将比std::numeric_limits<long>::max()大1。

图书馆作者可以做些什么来解决这个问题?

2 个答案:

答案 0 :(得分:19)

首先可以转换为无符号变体。这提供了明确定义的行为。如果相反,代码如下所示:

unsigned long abs(long input)
{
    if (input >= 0)
    {
        // input is positive
        return static_cast<unsigned long>(input);
    }
    else
    {
        return -static_cast<unsigned long>(input); // read on...
    }
}

我们调用两个定义良好的操作。将有符号整数转换为无符号整数由N3485 4.7 [conv.integral] / 2明确定义:

  

如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 ^ n,其中n是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断)。 - 结束说明]

这基本上说,当进行从有符号到无符号的特定转换时,可以假定无符号样式的回绕。

无符号整数的否定由5.3.1 [expr.unary.op] / 8明确定义:

  

无符号数量的负数是通过从2 ^ n减去其值来计算的,其中n是提升操作数中的位数。

这两个要求有效地迫使实现像2s补充机器那样操作,即使底层机器是1s补码或有符号幅度的机器。

答案 1 :(得分:0)

如果是负数,只需加一个。

unsigned long absolute_value(long x) {
  if (x >= 0) return (unsigned long)x;
  x = -(x+1);
  return (unsigned long)x + 1;
}