我有一些金融应用程序的定点实现。它基本上是包装在类中的整数,该类基于将N
给定的十进制数作为十进制数进行处理。该类是偏执狂并检查溢出,但是当我在发布模式下运行测试时,它们失败了,最后我创建了这个最小的示例来演示问题:
#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。它在那里不会引发异常。
请注意,该异常是在调试模式下引发的,但不在发布模式下。
答案 0 :(得分: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。尽管如此,所提供的函数也适用于signed
和unsigned
T
类型。
答案 1 :(得分:4)
如果要检测(签名)integer overflows(在标量类型为int64_t
或long
上),则应使用适当的内置程序,通常是特定于编译器的。
对于GCC,请参阅integer overflow builtins。
整数溢出(在普通int
或long
或其他有符号整数类型上)是undefined behavior的一个实例,因此编译器可以针对它进行优化。是scared。如果您依赖UB,则不再需要使用标准C ++进行编码,并且您的程序已绑定到特定的编译器和系统,那么根本就不会portable(甚至绑定到其他编译器,其他编译器版本,其他编译标志,其他)。计算机和操作系统)。因此,允许Clang(或GCC)针对整数溢出进行优化,有时甚至可以做到。
或者考虑使用一些bignum包(那么,您当然不只处理预定义的C ++整数标量类型)。也许GMPlib。
如果您的数字适合128位,则可以考虑使用GCC的__int128
。
我相信您无法可靠地检测到整数溢出(除非您使用integer overflow builtins)。您应该避免使用它们(或使用某些bignum库或使用这些内置函数的某些库等)。