[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'.
- [lmi-commits] [lmi] master updated (9f0eebc -> ed689f9), Greg Chicares, 2018/05/25
- [lmi-commits] [lmi] master 43d57bf 1/6: Consolidate and improve commentary, Greg Chicares, 2018/05/25
- [lmi-commits] [lmi] master e21dd5c 5/6: Refactor: use outward_quotient(), asserting equivalence, Greg Chicares, 2018/05/25
- [lmi-commits] [lmi] master ed689f9 6/6: Remove temporary assertions added in the last commit, Greg Chicares, 2018/05/25
- [lmi-commits] [lmi] master 1f212b8 4/6: Rename math_functors* to math_functions*, Greg Chicares, 2018/05/25
- [lmi-commits] [lmi] master ce477e3 3/6: Add a utility function for integer division rounded outward,
Greg Chicares <=
- [lmi-commits] [lmi] master 4c4abe6 2/6: Store specimen font measurements statically for reference, Greg Chicares, 2018/05/25