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

标签 c++ integer integer-overflow absolute-value signed-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 .

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

最佳答案

可以先强制转换为无符号变体以避免任何未定义的行为:

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=undefineduabs(std::numeric_limits<long>::min()); 中找不到 UB在不断传播之后,但在 std::abs() .

更多的模板内容应该可以创建一个返回整数类型的无符号版本,但返回 T 的版本。对于浮点类型,如果您想要 std::abs 的通用替换.

关于c++ - 有没有一种安全的方法来获取有符号整数的无符号绝对值,而不会触发溢出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17313579/

相关文章:

c++ - QGraphicsSimpleTextItem 抗锯齿不起作用

c++ - 将字符串存储在数组中以在 IF 语句中使用的可能方法

c++ - 带 alpha 的 DrawPrimitive

c++ - 这个网络库可以在 iOS 上运行吗?

java - 如何将十六进制颜色字符串解析为整数

java - Java中的整数到两位十六进制

c - INT_MAX 和 INT_MAX 之和

c# - 将整数输入保存到数组中

c++ - 在 int 中存储大于 INT_MAX 的数字

c++ - 模数是否溢出?