diff --git a/doc/gcd/math-gcd.qbk b/doc/gcd/math-gcd.qbk index 2edd2d55..20809bc7 100644 --- a/doc/gcd/math-gcd.qbk +++ b/doc/gcd/math-gcd.qbk @@ -150,6 +150,7 @@ They are also declared `noexcept` when appropriate. The boost::integer::gcd function template returns the greatest common (nonnegative) divisor of the two integers passed to it. +The nonnegative result must be representable by `IntegerType`. `boost::integer::gcd_range` is the iteration of the above gcd algorithm over a range, returning the greatest common divisor of all the elements. The algorithm terminates when the gcd reaches unity or the end of the range. Thus it also diff --git a/include/boost/integer/common_factor_rt.hpp b/include/boost/integer/common_factor_rt.hpp index 817682f2..2981c80d 100644 --- a/include/boost/integer/common_factor_rt.hpp +++ b/include/boost/integer/common_factor_rt.hpp @@ -440,10 +440,16 @@ namespace boost { template inline BOOST_CXX14_CONSTEXPR Integer gcd(Integer const &a, Integer const &b) BOOST_GCD_NOEXCEPT(Integer) { + // gcd(min, -1) is 1, but min % -1 overflows. Only a signed type has a negative unit: + // static_cast(-1) is the largest value of an unsigned type and must not match. if(a == (std::numeric_limits::min)()) - return a == static_cast(0) ? gcd_detail::gcd_traits::abs(b) : boost::integer::gcd(static_cast(a % b), b); + return a == static_cast(0) ? gcd_detail::gcd_traits::abs(b) + : (std::numeric_limits::is_signed && b == static_cast(-1)) ? static_cast(1) + : boost::integer::gcd(static_cast(a % b), b); else if (b == (std::numeric_limits::min)()) - return b == static_cast(0) ? gcd_detail::gcd_traits::abs(a) : boost::integer::gcd(a, static_cast(b % a)); + return b == static_cast(0) ? gcd_detail::gcd_traits::abs(a) + : (std::numeric_limits::is_signed && a == static_cast(-1)) ? static_cast(1) + : boost::integer::gcd(a, static_cast(b % a)); return gcd_detail::optimal_gcd_select(static_cast(gcd_detail::gcd_traits::abs(a)), static_cast(gcd_detail::gcd_traits::abs(b))); } diff --git a/test/common_factor_test.cpp b/test/common_factor_test.cpp index fd949133..d6a136c6 100644 --- a/test/common_factor_test.cpp +++ b/test/common_factor_test.cpp @@ -301,6 +301,15 @@ template< class T > void gcd_int_test() // signed_test_types BOOST_TEST_EQ(gcd_evaluator()(3, 7), static_cast(1)); BOOST_TEST_EQ(gcd_evaluator()(8, 9), static_cast(1)); BOOST_TEST_EQ(gcd_evaluator()(7, 49), static_cast(7)); + + if (std::numeric_limits::is_bounded && std::numeric_limits::is_signed) + { + const T minimum = (std::numeric_limits::min)(); + BOOST_TEST_EQ(boost::integer::gcd(minimum, static_cast(-1)), static_cast(1)); + BOOST_TEST_EQ(boost::integer::gcd(static_cast(-1), minimum), static_cast(1)); + BOOST_TEST_EQ(gcd_evaluator()(minimum, static_cast(-1)), static_cast(1)); + BOOST_TEST_EQ(gcd_evaluator()(static_cast(-1), minimum), static_cast(1)); + } } // GCD on unmarked signed integer type @@ -353,6 +362,14 @@ template< class T > void gcd_unsigned_test() // unsigned_test_types BOOST_TEST_EQ( boost::integer::gcd(static_cast(3u), static_cast(7u)), static_cast( 1u) ); BOOST_TEST_EQ( boost::integer::gcd(static_cast(8u), static_cast(9u)), static_cast( 1u) ); BOOST_TEST_EQ( boost::integer::gcd(static_cast(7u), static_cast(49u)), static_cast( 7u) ); + + // An unsigned type has no negative unit: static_cast(-1) is its largest + // value, and the GCD of zero and that value is that value, not one. + const T largest = static_cast(-1); + BOOST_TEST_EQ( boost::integer::gcd(static_cast(0u), largest), largest ); + BOOST_TEST_EQ( boost::integer::gcd(largest, static_cast(0u)), largest ); + BOOST_TEST_EQ( boost::integer::gcd_evaluator()(static_cast(0u), largest), largest ); + BOOST_TEST_EQ( boost::integer::gcd_evaluator()(largest, static_cast(0u)), largest ); } // GCD at compile-time diff --git a/test/gcd_constexpr14_test.cpp b/test/gcd_constexpr14_test.cpp index 956266f2..342fda2e 100644 --- a/test/gcd_constexpr14_test.cpp +++ b/test/gcd_constexpr14_test.cpp @@ -5,6 +5,7 @@ // https://www.boost.org/LICENSE_1_0.txt) #include +#include #ifndef BOOST_NO_CXX14_CONSTEXPR @@ -61,6 +62,29 @@ void test_constexpr5() static_assert(k == 347, "Expected result not integer in constexpr gcd."); } + +void test_constexpr_minimum_unit() +{ + constexpr int minimum = (std::numeric_limits::min)(); + static_assert(boost::integer::gcd(minimum, -1) == 1, "Expected gcd(min, -1) == 1."); + static_assert(boost::integer::gcd(-1, minimum) == 1, "Expected gcd(-1, min) == 1."); + + constexpr boost::int64_t minimum64 = (std::numeric_limits::min)(); + static_assert(boost::integer::gcd(minimum64, boost::int64_t(-1)) == 1, "Expected gcd(min64, -1) == 1."); + static_assert(boost::integer::gcd(boost::int64_t(-1), minimum64) == 1, "Expected gcd(-1, min64) == 1."); +} + +void test_constexpr_unsigned_maximum() +{ + // The minimum of an unsigned type is zero and unsigned(-1) is its maximum. + constexpr unsigned maximum = (std::numeric_limits::max)(); + static_assert(boost::integer::gcd(0u, maximum) == maximum, "Expected gcd(0, max) == max."); + static_assert(boost::integer::gcd(maximum, 0u) == maximum, "Expected gcd(max, 0) == max."); + + constexpr boost::uint64_t maximum64 = (std::numeric_limits::max)(); + static_assert(boost::integer::gcd(boost::uint64_t(0), maximum64) == maximum64, "Expected gcd(0, max64) == max64."); + static_assert(boost::integer::gcd(maximum64, boost::uint64_t(0)) == maximum64, "Expected gcd(max64, 0) == max64."); +} #endif