diff --git a/include/CLI/Validators.hpp b/include/CLI/Validators.hpp index e9e9648d1..6c9f2a34e 100644 --- a/include/CLI/Validators.hpp +++ b/include/CLI/Validators.hpp @@ -1,5 +1,4 @@ #pragma once - // Distributed under the 3-Clause BSD License. See accompanying // file LICENSE or https://github.com/CLIUtils/CLI11 for details. @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -512,17 +512,37 @@ auto search(const T &set, const V &val, const std::function &filter_functi return {(it != std::end(setref)), it}; } +// 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); +} + /// 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) { - if(a == 0 || b == 0) { + if(a == 0 || b == 0 || a == 1 || b == 1) { a *= b; return true; } - T c = a * b; - if(c / a != b) { + if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { return false; } - a = c; + if(overflowCheck(a, b)) { + return false; + } + a *= b; return true; } diff --git a/tests/HelpersTest.cpp b/tests/HelpersTest.cpp index bf1b44a09..5495cccf0 100644 --- a/tests/HelpersTest.cpp +++ b/tests/HelpersTest.cpp @@ -423,10 +423,34 @@ TEST(CheckedMultiply, Int) { ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); ASSERT_EQ(a, std::numeric_limits::min()); + b = std::numeric_limits::min(); + a = -1; + ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); + ASSERT_EQ(a, -1); + a = std::numeric_limits::min() / 100; b = 99; ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); ASSERT_EQ(a, std::numeric_limits::min() / 100 * 99); + + a = std::numeric_limits::min() / 100; + 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) {