考虑一个典型的绝对值函数(其中为参数的缘故整体式最大尺寸的变长):
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大。
什么可以在库的作者做来解决这个问题呢?
人们可以转换为无符号变型第一。这提供了明确的行为。相反,如果该代码如下所示:
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的补码或符号的振幅机操作。
只需添加一个如果为负。
unsigned long absolute_value(long x) {
if (x >= 0) return (unsigned long)x;
x = -(x+1);
return (unsigned long)x + 1;
}