优化正在取消我在 clang 6 中的整数溢出检查
Optimizations are killing my integer overflow checks in clang 6
我有一些金融应用的定点实现。它基本上是一个包含在 class 中的整数,它基于给定的小数位数 N
被视为十进制数。 class 是 pa运行oid 并检查溢出,但是当我 运行 在发布模式下测试时,它们失败了,最后我创建了这个演示问题的最小示例:
#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。它不会在那里抛出异常。
请注意,在调试模式下会抛出异常,但不会在发布模式下抛出。
如果您想检测(有符号)integer overflows(在 int64_t
或 long
等标量类型上),您应该使用适当的内置函数,通常是特定于编译器的。
对于 GCC,请参阅 integer overflow builtins。
整数溢出(在 int
或 long
或其他有符号整数类型上)根本是 undefined behavior, so the compiler can optimize as it please against it. Be scared. If you depend on UB you are no more coding in standard C++ and your program is tied to a particular compiler and system, so is not portable 的一个实例(甚至对其他编译器、其他编译器版本、其他编译标志、其他计算机和操作系统)。所以 Clang(或 GCC)被允许针对整数溢出进行优化,有时确实如此。
或者考虑使用一些bignum package (then of course you don't deal with just predefined C++ integral scalar types). Perhaps GMPlib。
如果您的数字适合 128 位,您可以考虑使用 GCC 的 __int128
。
我相信当整数溢出发生时您无法可靠地检测到它们(除非您使用 integer overflow builtins)。你应该避免使用它们(或者使用一些 bignum 库,或者一些使用这些内置函数的库等)。
正如 @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;
}
}
函数returnstrue
如果整数乘法x * y
预测溢出
请注意,虽然 unsigned
溢出是根据 modular arithmetic, signed
overflow is an undefined behavior 明确定义的。尽管如此,所提供的函数也适用于 signed
和 unsigned
T
类型。
我有一些金融应用的定点实现。它基本上是一个包含在 class 中的整数,它基于给定的小数位数 N
被视为十进制数。 class 是 pa运行oid 并检查溢出,但是当我 运行 在发布模式下测试时,它们失败了,最后我创建了这个演示问题的最小示例:
#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。它不会在那里抛出异常。
请注意,在调试模式下会抛出异常,但不会在发布模式下抛出。
如果您想检测(有符号)integer overflows(在 int64_t
或 long
等标量类型上),您应该使用适当的内置函数,通常是特定于编译器的。
对于 GCC,请参阅 integer overflow builtins。
整数溢出(在 int
或 long
或其他有符号整数类型上)根本是 undefined behavior, so the compiler can optimize as it please against it. Be scared. If you depend on UB you are no more coding in standard C++ and your program is tied to a particular compiler and system, so is not portable 的一个实例(甚至对其他编译器、其他编译器版本、其他编译标志、其他计算机和操作系统)。所以 Clang(或 GCC)被允许针对整数溢出进行优化,有时确实如此。
或者考虑使用一些bignum package (then of course you don't deal with just predefined C++ integral scalar types). Perhaps GMPlib。
如果您的数字适合 128 位,您可以考虑使用 GCC 的 __int128
。
我相信当整数溢出发生时您无法可靠地检测到它们(除非您使用 integer overflow builtins)。你应该避免使用它们(或者使用一些 bignum 库,或者一些使用这些内置函数的库等)。
正如 @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;
}
}
函数returnstrue
如果整数乘法x * y
预测溢出
请注意,虽然 unsigned
溢出是根据 modular arithmetic, signed
overflow is an undefined behavior 明确定义的。尽管如此,所提供的函数也适用于 signed
和 unsigned
T
类型。