lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 9f092e6 2/5: Expunge CURRENCY_UNIT_IS_CENTS


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 9f092e6 2/5: Expunge CURRENCY_UNIT_IS_CENTS
Date: Fri, 5 Mar 2021 19:16:19 -0500 (EST)

branch: master
commit 9f092e6a9c494dad8c53e09cbe332ba7a5f6b58c
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Expunge CURRENCY_UNIT_IS_CENTS
    
    The currency unit is now always cents (as opposed to dollars).
---
 currency.hpp      | 38 +++++++++++++++-----------------------
 currency_test.cpp |  5 -----
 ihs_acctval.cpp   | 18 +++++-------------
 ihs_avmly.cpp     |  8 --------
 loads_test.cpp    |  4 ----
 round_to.hpp      | 20 ++++++++++++++++----
 round_to_test.cpp |  5 -----
 7 files changed, 36 insertions(+), 62 deletions(-)

diff --git a/currency.hpp b/currency.hpp
index 11009fa..1837b5d 100644
--- a/currency.hpp
+++ b/currency.hpp
@@ -29,14 +29,13 @@
 #include <stdexcept>                    // runtime_error
 #include <vector>
 
-// Macros USE_CURRENCY_CLASS and CURRENCY_UNIT_IS_CENTS are used
-// elsewhere. Eventually they'll both be eliminated, along with
-// all code along paths where they aren't both defined.
+// Macro USE_CURRENCY_CLASS is used
+// elsewhere. Eventually it'll be eliminated, along with
+// all code along paths where it isn't defined.
 
 #define USE_CURRENCY_CLASS
 
 #if !defined USE_CURRENCY_CLASS
-#   undef CURRENCY_UNIT_IS_CENTS // Requires currency class.
 
 using currency = double;
 
@@ -51,8 +50,6 @@ inline std::vector<double> dblize(std::vector<currency> 
const& z)
 
 #else // defined USE_CURRENCY_CLASS
 
-#   define CURRENCY_UNIT_IS_CENTS
-
 class raw_cents {}; // Tag class.
 
 class currency
@@ -62,13 +59,8 @@ class currency
     template<typename> friend class round_to; // explicit ctor
     friend class round_to_test;               // currency::cents_digits
 
-#   if defined CURRENCY_UNIT_IS_CENTS
     static constexpr int    cents_digits     = 2;
     static constexpr double cents_per_dollar = 100.0;
-#   else  // !defined CURRENCY_UNIT_IS_CENTS
-    static constexpr int    cents_digits     = 0;
-    static constexpr double cents_per_dollar = 1.0;
-#   endif // !defined CURRENCY_UNIT_IS_CENTS
 
   public:
     using data_type = double;
@@ -131,19 +123,19 @@ inline double operator/(currency lhs, currency rhs)
 inline std::ostream& operator<<(std::ostream& os, currency z)
     {return os << z.d();}
 
-inline currency from_cents(double z)
+/// Convert from an integer-valued double to currency.
+///
+/// This function is intended to be called very seldom (and then
+/// almost always with a manifest-constant argument), so the cost
+/// of the runtime value-preservation test doesn't matter.
+
+inline currency from_cents(double cents)
     {
-#   if defined CURRENCY_UNIT_IS_CENTS
-    if(z != std::rint(z)) throw std::runtime_error("Nonintegral cents.");
-    return currency(z, raw_cents{});
-#   else  // !defined CURRENCY_UNIT_IS_CENTS
-    // If currency unit is dollars rather than cents, then:
-    //  - dividing by 100 is the only reasonable thing to do here, even
-    //    though 'cents_per_dollar' is unity; and
-    //  - a value such as $.01 cannot be integral, so the desired
-    //    invariant that the result is integral must be sacrificed.
-    return currency(z / 100.0, raw_cents{});
-#   endif // !defined CURRENCY_UNIT_IS_CENTS
+    if(cents != std::rint(cents))
+        {
+        throw std::runtime_error("Nonintegral cents.");
+        }
+    return currency(cents, raw_cents{});
     }
 
 inline double dblize(currency z) {return z.d();}
diff --git a/currency_test.cpp b/currency_test.cpp
index 1d109ae..f20ffc8 100644
--- a/currency_test.cpp
+++ b/currency_test.cpp
@@ -69,10 +69,6 @@ void currency_test::test()
     test_copy_ctor();
     test_explicit_ctor();
     test_negation();
-// CURRENCY !! Most of these tests assume that the currency unit is
-// cents. It's not worth adapting them to any other case because
-// soon this macro will, in effect, always be defined.
-#   if defined CURRENCY_UNIT_IS_CENTS
     test_plus_or_minus_eq();
     test_plus_or_minus();
     test_multiply_by_int();
@@ -85,7 +81,6 @@ void currency_test::test()
     test_round_currency();
     test_infinite();
     test_quodlibet();
-#   endif // defined CURRENCY_UNIT_IS_CENTS
 #endif // defined USE_CURRENCY_CLASS
 }
 
diff --git a/ihs_acctval.cpp b/ihs_acctval.cpp
index 88173a8..3501d29 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -180,20 +180,12 @@ currency AccountValue::specamt_for_7702A(int year) const
 
 void AccountValue::assert_pmts_add_up(char const* file, int line, int month)
 {
-    // If the currency unit is cents (as it ultimately will be), then
-    // all currency amounts should be an exact integral number of
-    // cents, and payments should add up exactly. For the nonce, the
-    // currency unit might not be cents, in which case payments should
-    // add up within a tiny tolerance.
-    bool const okay =
-#if defined CURRENCY_UNIT_IS_CENTS
-                         GrossPmts[month] ==   EeGrossPmts[month]     + 
ErGrossPmts[month]
-#else  // !defined CURRENCY_UNIT_IS_CENTS
-        materially_equal(dblize(GrossPmts[month]), dblize(EeGrossPmts[month]) 
+ dblize(ErGrossPmts[month]))
-#endif // !defined CURRENCY_UNIT_IS_CENTS
-        ;
-    if(okay)
+    // Payments, being currency amounts, should all be exact integral
+    // numbers of cents, and should add up exactly.
+    if(GrossPmts[month] == EeGrossPmts[month] + ErGrossPmts[month])
+        {
         return;
+        }
 
     alarum()
         << "Payments don't add up [file '" << file << "', line " << line << 
"]\n"
diff --git a/ihs_avmly.cpp b/ihs_avmly.cpp
index e63ca57..2b58577 100644
--- a/ihs_avmly.cpp
+++ b/ihs_avmly.cpp
@@ -349,10 +349,6 @@ void AccountValue::IncrementAVProportionally(currency 
increment)
     currency genacct_increment = round_minutiae().c(increment * 
GenAcctPaymentAllocation);
     AVGenAcct += genacct_increment;
     AVSepAcct += increment - genacct_increment;
-#if !defined CURRENCY_UNIT_IS_CENTS
-    AVSepAcct = round_minutiae().c(dblize(AVSepAcct));
-    if(0.0 == dblize(AVSepAcct)) AVSepAcct = C0; // Negate negative zeroes.
-#endif // !defined CURRENCY_UNIT_IS_CENTS
 }
 
 //============================================================================
@@ -482,10 +478,6 @@ void AccountValue::DecrementAVProportionally(currency 
decrement)
     currency genacct_decrement = round_minutiae().c(decrement * 
general_account_proportion);
     AVGenAcct -= genacct_decrement;
     AVSepAcct -= decrement - genacct_decrement;
-#if !defined CURRENCY_UNIT_IS_CENTS
-    AVSepAcct = round_minutiae().c(dblize(AVSepAcct));
-    if(0.0 == dblize(AVSepAcct)) AVSepAcct = C0; // Negate negative zeroes.
-#endif // !defined CURRENCY_UNIT_IS_CENTS
 }
 
 /// Apportion decrements to account value between separate- and
diff --git a/loads_test.cpp b/loads_test.cpp
index 460fd72..3c5f2d1 100644
--- a/loads_test.cpp
+++ b/loads_test.cpp
@@ -160,11 +160,7 @@ void LoadsTest::TestCalculations(char const* file, int 
line)
     INVOKE_LMI_TEST(materially_equal(0.500000, 
loads_.refundable_sales_load_proportion()[0]), file, line);
 
     // (8.00 + 5.25 + 0.50) / 2 = 13.75 / 2 = 6.875, rounded to cents
-#   if defined CURRENCY_UNIT_IS_CENTS
     INVOKE_LMI_TEST(from_cents(688) == loads_.monthly_policy_fee 
(mce_gen_mdpt)[0] , file, line);
-#   else  // !defined CURRENCY_UNIT_IS_CENTS
-    INVOKE_LMI_TEST(materially_equal(6.88, dblize(loads_.monthly_policy_fee 
(mce_gen_mdpt)[0])), file, line);
-#   endif // !defined CURRENCY_UNIT_IS_CENTS
     INVOKE_LMI_TEST(from_cents(150) == loads_.annual_policy_fee  
(mce_gen_mdpt)[0] , file, line);
     INVOKE_LMI_TEST(materially_equal(0.000625, loads_.specified_amount_load 
(mce_gen_mdpt)[0]), file, line);
     // 12 bp and 19 bp, both converted to monthly, then added together.
diff --git a/round_to.hpp b/round_to.hpp
index d1f63b0..053f08d 100644
--- a/round_to.hpp
+++ b/round_to.hpp
@@ -417,14 +417,26 @@ inline std::vector<currency> round_to<RealType>::c
 
 #if defined USE_CURRENCY_CLASS
 // CURRENCY !! need unit tests
+
+/// Round currency to a potentially different precision.
+///
+/// In practice, lmi rounds almost all currency values to cents, and
+/// rounding again to cents appropriately does nothing. But it rounds
+/// some currency values to dollars (as configured in a '.rounding'
+/// file that can be edited); rounding eleven cents to the nearest
+/// dollar, e.g., must change the value.
+///
+/// This implementation does that as follows:
+///   11 cents --> 0.11 (double)
+///   0.11 --> 0 dollars (nearest)
+/// Roundoff error in the first step doesn't matter. The critical
+/// points for all rounding directions are some whole number plus
+/// zero or one-half, which involve no roundoff error.
+
 template<typename RealType>
 inline currency round_to<RealType>::c(currency z) const
 {
-#   if defined CURRENCY_UNIT_IS_CENTS
     return (decimals_ < currency::cents_digits) ? c(z.d()) : z;
-#   else  // !defined CURRENCY_UNIT_IS_CENTS
-    return c(z.d());
-#   endif // !defined CURRENCY_UNIT_IS_CENTS
 }
 
 template<typename RealType>
diff --git a/round_to_test.cpp b/round_to_test.cpp
index 3c66b81..22266cd 100644
--- a/round_to_test.cpp
+++ b/round_to_test.cpp
@@ -581,12 +581,7 @@ void round_to_test::test_fundamentals()
     currency c = round0.c(1.61803398875);
     LMI_TEST((1.62 - dblize(c)) < 1e-14);
 #if defined USE_CURRENCY_CLASS
-#   if defined CURRENCY_UNIT_IS_CENTS
     LMI_TEST_EQUAL(162, c.cents());
-#   else  // !defined CURRENCY_UNIT_IS_CENTS
-    // Arguably this isn't quite meaningful:
-    LMI_TEST_EQUAL(1.62, c.cents());
-#   endif // !defined CURRENCY_UNIT_IS_CENTS
 #endif // defined USE_CURRENCY_CLASS
 //  c *= 0.61803398875;
 //  LMI_TEST_EQUAL(1, c);



reply via email to

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