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

问题描述 投票:12回答:2

考虑一个典型的绝对值函数(其中为参数的缘故整体式最大尺寸的变长):

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的否定可能溢出,并触发符号整数溢出是未定义的行为。例如,在二进制补码机器,std::numeric_limits<long>::min()的绝对值会比std::numeric_limits<long>::max() 1大。

什么可以在库的作者做来解决这个问题呢?

c++ integer integer-overflow
2个回答
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是在促进的操作数的比特数中减去其值来计算。

这两个要求有效强制实施到像一个二进制补码机会,即使底层机器是1的补码或符号的振幅机操作。


0
投票

只需添加一个如果为负。

unsigned long absolute_value(long x) {
  if (x >= 0) return (unsigned long)x;
  x = -(x+1);
  return (unsigned long)x + 1;
}
© www.soinside.com 2019 - 2024. All rights reserved.