diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index fe5410094e..8f8bd204f2 100755 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1439,6 +1439,29 @@ BOOST_AUTO_TEST_CASE(util_Fraction_multiplication_with_internal_simplification) BOOST_CHECK_EQUAL(product.IsSimplified(), true); } +BOOST_AUTO_TEST_CASE(util_Fraction_multiplication_with_cross_simplification_overflow_resistance) +{ + + Fraction lhs(std::numeric_limits::max() - 3, std::numeric_limits::max() - 1, false); + Fraction rhs((std::numeric_limits::max() - 1) / (int64_t) 2, (std::numeric_limits::max() - 3) / (int64_t) 2); + + Fraction product; + + // This should NOT overflow + bool overflow = false; + try { + product = lhs * rhs; + } catch (std::overflow_error& e) { + overflow = true; + } + + BOOST_CHECK_EQUAL(overflow, false); + + if (!overflow) { + BOOST_CHECK(product == Fraction(1)); + } +} + BOOST_AUTO_TEST_CASE(util_Fraction_division_with_internal_simplification) { Fraction lhs(-2, 3); diff --git a/src/util.h b/src/util.h index c591792c18..fa5b2f6a8e 100644 --- a/src/util.h +++ b/src/util.h @@ -435,8 +435,36 @@ class Fraction { Fraction slhs(*this, true); Fraction srhs(rhs, true); - return Fraction(overflow_mult(slhs.GetNumerator(), srhs.GetNumerator()), - overflow_mult(slhs.GetDenominator(), srhs.GetDenominator()), + // Gcd's can be used in multiplication for better overflow resistance as well. + // + // Consider + // a c + // - * -, where a/b and c/d are already simplified (i.e. gcd(a, b) = gcd(c, d) = 1. + // b d + // + // We can have g = gcd(a, d) and h = gcd(c, b), which is with the numerators reversed, since multiplication is + // commutative. This means we have + // + // (c / h) (a / g) + // ------- * ------- . + // (b / h) (d / g) + // + // If we form Fraction(c, b, true) and Fraction(a, d, true), the simplication will determine and divide the numerator and + // denominator by h and g respectively. + // + // A specific example is instructive. + // + // 1998 1000 999 1000 1000 999 1 1 + // ---- * ---- = ---- * ---- = ---- * --- = - * - + // 2000 999 1000 999 1000 999 1 1 + // + // This is a formal form of what grade school teachers called factor cancellation. :). + + Fraction sxlhs(srhs.GetNumerator(), slhs.GetDenominator(), true); + Fraction sxrhs(slhs.GetNumerator(), srhs.GetDenominator(), true); + + return Fraction(overflow_mult(sxlhs.GetNumerator(), sxrhs.GetNumerator()), + overflow_mult(sxlhs.GetDenominator(), sxrhs.GetDenominator()), true); }