优化正在扼杀我在clang 6中的整数溢出检查

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

我有一些针对某些财务应用程序的定点实现。它基本上是一个包含在类中的整数,它基于给定Ntreated作为十进制数的小数位数。该类是偏执的并检查溢出,但是当我在发布模式下运行我的测试时,它们都失败了,最后我创建了这个演示问题的最小例子:

#include <iostream>
#include <sstream>

template <typename T, typename U>
typename std::enable_if<std::is_convertible<U, std::string>::value, T>::type 
FromString(U&& str)
{
    std::stringstream ss;
    ss << str;
    T ret;
    ss >> ret;
    return ret;
}

int main()
{
    int NewAccu=32;
    int N=10;

    using T = int64_t;

    T l = 10;
    T r = FromString<T>("1" + std::string(NewAccu - N, '0'));
    if (l == 0 || r == 0) {
        return 0;
    }
    T res = l * r;
    std::cout << l << std::endl;
    std::cout << r << std::endl;
    std::cout << res << std::endl;
    std::cout << (res / l) << std::endl;
    std::cout << std::endl;
    if ((res / l) != r) {
        throw std::runtime_error(
                   "FixedPoint Multiplication Overflow while upscaling [:" + std::to_string(l) + ", " + std::to_string(r) + "]");
    }

    return 0;
}

Clang 6会发生这种情况,我的版本是:

$ clang++ --version
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

这很有趣,因为它是一个令人印象深刻的优化,但这破坏了我的应用程序,并防止我检测溢出。我能够重现这个问题in g++ here。它不会在那里抛出异常。

请注意,在调试模式下抛出异常,但它不在释放模式下。

c++ c++11 optimization integer-overflow fixed-point
2个回答
9
投票

正如@Basile所说,有符号整数溢出是一种未定义的行为,因此编译器可以以任何方式处理它 - 甚至优化它以获得性能优势。因此在发生整数溢出后检测它是否为时已晚。相反,您应该在它发生之前预测整数溢出。

这是我对整数乘法的溢出预测的实现:

#include <limits>

template <typename T>
bool predict_mul_overflow(T x, T y)
{
    static_assert(std::numeric_limits<T>::is_integer, "predict_mul_overflow expects integral types");

    if constexpr (std::numeric_limits<T>::is_bounded)
    {
        return ((x != T{0}) && ((std::numeric_limits<T>::max() / x) < y));
    }
    else
    {
        return false;
    }
}

如果预测整数乘法true溢出,则函数返回x * y

请注意,虽然unsigned溢出在modular arithmetic方面是明确定义的,但signed溢出是undefined behavior。尽管如此,所提出的函数也适用于signedunsigned T类型。


4
投票

如果你想检测(签名)integer overflows(在int64_tlong等标量类型上),你应该使用适当的内置函数,通常是编译器特定的。

对于海湾合作委员会,请参阅integer overflow builtins

整数溢出(在普通的intlong或其他有符号整数类型上)是undefined behavior的一个实例,因此编译器可以根据需要进行优化。是scared。如果您依赖于UB,那么您不再使用标准C ++进行编码,并且您的程序与特定的编译器和系统相关联,因此根本不是portable(甚至是其他编译器,其他编译器版本,其他编译标志,其他计算机和操作系统) 。因此允许Clang(或GCC)优化整数溢出,有时也可以。

或者考虑使用一些bignum包(当然你不处理预定义的C ++积分标量类型)。也许GMPlib

如果您的数字符合128位,您可以考虑使用GCC的__int128

我相信当它们发生时你不能可靠地检测整数溢出(除非你使用integer overflow builtins)。你应该避免它们(或使用一些bignum库,或使用这些内置函数的一些库等)。

© www.soinside.com 2019 - 2024. All rights reserved.