考虑一个典型的绝对值函数(为了论证,最大大小的整数类型是长的):
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 .
图书馆作者可以做些什么来解决这个问题?
最佳答案
可以先强制转换为无符号变体以避免任何未定义的行为:
unsigned long uabs(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 很好地定义了将有符号整数转换为无符号整数:
If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note ]
这基本上是说,在进行从有符号到无符号的特定转换时,可以假设无符号样式的环绕。
5.3.1 [expr.unary.op]/8 很好地定义了无符号整数的否定:
The negative of an unsigned quantity is computed by subtracting its value from 2^n , where n is the number of bits in the promoted operand.
这两个要求有效地强制实现像 2s 补码机器一样运行,即使底层机器是 1s 补码或有符号幅度机器。
返回整数类型的无符号版本的通用 C++11 版本:
#include <type_traits>
template <typename T>
constexpr
typename std::make_unsigned<T>::type uabs(T x)
{
typename std::make_unsigned<T>::type ux = x;
return (x<0) ? -ux : ux; // compare signed x, negate unsigned x
}
编译 on the Godbolt compiler explorer ,测试用例显示 gcc -O3 -fsanitize=undefined
在 uabs(std::numeric_limits<long>::min());
中找不到 UB在不断传播之后,但在 std::abs()
.
更多的模板内容应该可以创建一个返回整数类型的无符号版本,但返回 T
的版本。对于浮点类型,如果您想要 std::abs
的通用替换.
关于c++ - 有没有一种安全的方法来获取有符号整数的无符号绝对值,而不会触发溢出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17313579/