I have a fixed-point implementation for some financial application. It's basically an integer wrapped in a class that is based on the number of decimals given N
treated as a decimal number. The class is paranoid and checks for overflows, but when I ran my tests in release mode, and they failed, and finally I created this minimal example that demonstrates the problem:
#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;
}
This happens with Clang 6, my version is:
$ clang++ --version
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
It's funny because it's an impressive optimization, but this ruins my application and prevents me from detecting overflows. I was able to reproduce this problem in g++ here. It doesn't throw an exception there.
Notice that the exception is thrown in debug mode, but it's not in release mode.
As @Basile already stated, signed integer overflow is an undefined behavior, so the compiler can handle it in any way - even optimizing it away to gain a performance advantage. So detecting integer overflow after its occurence is way too late. Instead, you should predict integer overflow just before it occurs.
Here is my implementation of overflow prediction of integer multiplication:
#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;
}
}
The function returns true
if the integer multiplication x * y
is predicted to overflow.
Note that while unsigned
overflow is well-defined in terms of modular arithmetic, signed
overflow is an undefined behavior. Nevertheless, the presented function works for signed
and unsigned
T
types as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With