lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master ce477e3 3/6: Add a utility function for integ


From: Greg Chicares
Subject: [lmi-commits] [lmi] master ce477e3 3/6: Add a utility function for integer division rounded outward
Date: Fri, 25 May 2018 07:00:57 -0400 (EDT)

branch: master
commit ce477e344165bdce0728450f7e9870647c5f5500
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Add a utility function for integer division rounded outward
    
    This operation is often useful, and
      (numerator + denominator - 1) / denominator
    isn't perfectly transparent.
---
 math_functors.hpp      | 32 ++++++++++++++++++++++----
 math_functors_test.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/math_functors.hpp b/math_functors.hpp
index aa4fa1a..721742e 100644
--- a/math_functors.hpp
+++ b/math_functors.hpp
@@ -30,15 +30,16 @@
 #include <algorithm>                    // max(), min()
 #include <cmath>                        // expm1(), log1p()
 #include <functional>
+#include <limits>
 #include <stdexcept>
 #include <type_traits>
 #include <vector>
 
-// TODO ?? Write functors here for other refactorable uses of
-// std::pow() found throughout the program.
+// TODO ?? Write functions here for other refactorable uses of
+// std::pow() throughout lmi, to facilitate reuse and unit testing.
 
-// These functors are Adaptable Unary or Binary Functions wherever
-// possible.
+// Many of these are Adaptable Unary or Binary Functions because that
+// was good C++98 practice.
 
 template<typename T>
 struct greater_of
@@ -84,6 +85,29 @@ struct mean
         {return 0.5 * x + 0.5 * y;}
 };
 
+/// Divide integers, rounding away from zero.
+///
+/// This floating-point analogue may be useful for cross checking:
+///   long double z = (long double)numerator / (long double)denominator;
+///   return (T) (0 < z) ? std::ceil(z) : std::floor(z);
+
+template<typename T>
+inline T outward_quotient(T numerator, T denominator)
+{
+    static_assert(std::is_integral<T>::value);
+
+    LMI_ASSERT(0 != denominator);
+
+    // "INT_MIN / -1" would overflow; but "false/bool(-1)" would not,
+    // hence the "T(-1) < 0" test.
+    constexpr T min = std::numeric_limits<T>::min();
+    LMI_ASSERT(!(min == numerator && T(-1) < 0 && T(-1) == denominator));
+
+    T x = numerator / denominator;
+    T y = 0 != numerator % denominator;
+    return (0 < numerator == 0 < denominator) ? x + y : x - y;
+}
+
 // Actuarial functions.
 //
 // Some inputs are nonsense, like interest rates less than 100%.
diff --git a/math_functors_test.cpp b/math_functors_test.cpp
index b6ab695..462aa51 100644
--- a/math_functors_test.cpp
+++ b/math_functors_test.cpp
@@ -237,6 +237,8 @@ int test_main(int, char*[])
     BOOST_TEST_EQUAL(2.0, greater_of<double>()(1.0, 2.0));
     BOOST_TEST_EQUAL(1.0, lesser_of <double>()(1.0, 2.0));
 
+    // Test mean<>().
+
     BOOST_TEST_EQUAL(1.5, mean<double>()(1.0, 2.0));
     BOOST_TEST_EQUAL(smallnumD, mean<double>()(smallnumD, smallnumD));
     BOOST_TEST_EQUAL(bignumD  , mean<double>()(bignumD  , bignumD  ));
@@ -245,6 +247,66 @@ int test_main(int, char*[])
     BOOST_TEST_EQUAL(smallnumL, mean<long double>()(smallnumL, smallnumL));
     BOOST_TEST_EQUAL(bignumL  , mean<long double>()(bignumL  , bignumL  ));
 
+    // Test outward_quotient().
+
+    BOOST_TEST_EQUAL( 1, outward_quotient( 2,  2));
+    BOOST_TEST_EQUAL( 1, outward_quotient( 1,  2));
+    BOOST_TEST_EQUAL( 0, outward_quotient( 0,  2));
+    BOOST_TEST_EQUAL(-1, outward_quotient(-1,  2));
+    BOOST_TEST_EQUAL(-1, outward_quotient(-2,  2));
+
+    BOOST_TEST_EQUAL(-1, outward_quotient( 2, -2));
+    BOOST_TEST_EQUAL(-1, outward_quotient( 1, -2));
+    BOOST_TEST_EQUAL( 0, outward_quotient( 0, -2));
+    BOOST_TEST_EQUAL( 1, outward_quotient(-1, -2));
+    BOOST_TEST_EQUAL( 1, outward_quotient(-2, -2));
+
+    BOOST_TEST_EQUAL( 0ULL, outward_quotient( 0ULL,  2ULL));
+    BOOST_TEST_EQUAL( 1ULL, outward_quotient( 1ULL,  2ULL));
+    BOOST_TEST_EQUAL( 1ULL, outward_quotient( 2ULL,  2ULL));
+
+    BOOST_TEST_EQUAL( 0, outward_quotient( 0,  3));
+    BOOST_TEST_EQUAL( 1, outward_quotient( 1,  3));
+    BOOST_TEST_EQUAL( 1, outward_quotient( 2,  3));
+    BOOST_TEST_EQUAL( 1, outward_quotient( 3,  3));
+    BOOST_TEST_EQUAL( 2, outward_quotient( 4,  3));
+    BOOST_TEST_EQUAL( 2, outward_quotient( 5,  3));
+    BOOST_TEST_EQUAL( 2, outward_quotient( 6,  3));
+    BOOST_TEST_EQUAL( 3, outward_quotient( 7,  3));
+
+    BOOST_TEST_EQUAL(INT_MIN, outward_quotient(INT_MIN,       1));
+    BOOST_TEST_EQUAL(      1, outward_quotient(INT_MIN, INT_MIN));
+    BOOST_TEST_EQUAL(     -1, outward_quotient(      1, INT_MIN));
+
+    BOOST_TEST_EQUAL(INT_MAX, outward_quotient(INT_MAX,       1));
+    BOOST_TEST_EQUAL(      1, outward_quotient(INT_MAX, INT_MAX));
+    BOOST_TEST_EQUAL(      1, outward_quotient(      1, INT_MAX));
+
+    BOOST_TEST_EQUAL(UINT_MAX, outward_quotient(UINT_MAX,       1u));
+    BOOST_TEST_EQUAL(      1u, outward_quotient(UINT_MAX, UINT_MAX));
+    BOOST_TEST_EQUAL(      1u, outward_quotient(      1u, UINT_MAX));
+
+    // The language allows "false/true"; this is no sillier.
+    BOOST_TEST_EQUAL(false, outward_quotient(false, true));
+
+    BOOST_TEST_THROW
+        (outward_quotient(1, 0)
+        ,std::runtime_error
+        ,"Assertion '0 != denominator' failed."
+        );
+
+    BOOST_TEST_THROW
+        (outward_quotient(INT_MIN, -1)
+        ,std::runtime_error
+        ,lmi_test::what_regex("^Assertion.*failed")
+        );
+
+// Appropriately fails to compile due to conflicting types:
+//  outward_quotient( 1, 1u);
+
+// Appropriately fails to compile due to static assertion:
+//  outward_quotient(1.0, 1.0);
+
     // Actuarial functions.
 
     // Test with 1 == 'n'.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]