diff --git a/include/CLI/Validators.hpp b/include/CLI/Validators.hpp index 56f2c3737..6bba87466 100644 --- a/include/CLI/Validators.hpp +++ b/include/CLI/Validators.hpp @@ -509,12 +509,24 @@ auto search(const T &set, const V &val, const std::function &filter_functi }); return {(it != std::end(setref)), it}; } -/// Generate the absolute value of a number -template inline typename std::enable_if::value, T>::type absval(T a) { - return static_cast((std::abs)(a)); + +// the following suggestion was made by Nikita Ofitserov(@himikof) +// done in templates to prevent compiler warnings on negation of unsigned numbers + +/// Do a check for overflow on signed numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + if((a > 0) == (b > 0)) { + return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); + } else { + return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); + } +} +/// Do a check for overflow on unsigned numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + return ((std::numeric_limits::max)() / a < b); } -/// unsigned values just return the value -template inline typename std::enable_if::value, T>::type absval(T a) { return a; } /// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. template typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { @@ -525,7 +537,7 @@ template typename std::enable_if::value, bool>: if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { return false; } - if((std::numeric_limits::max)() / absval(a) < absval(b)) { + if(overflowCheck(a, b)) { return false; } a *= b; diff --git a/tests/HelpersTest.cpp b/tests/HelpersTest.cpp index 595e0804a..f6244bc63 100644 --- a/tests/HelpersTest.cpp +++ b/tests/HelpersTest.cpp @@ -437,6 +437,20 @@ TEST(CheckedMultiply, Int) { b = -101; ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); ASSERT_EQ(a, std::numeric_limits::min() / 100); + a = 2; + b = std::numeric_limits::min() / 2; + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); + a = std::numeric_limits::min() / 2; + b = 2; + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); + + a = 4; + b = std::numeric_limits::min() / 4; + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); + + a = 48; + b = std::numeric_limits::min() / 48; + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); } TEST(CheckedMultiply, SizeT) {