[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] odd/eraseme 8b99978 1/4: Expunge CURRENCY_UNIT_IS_CE
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] odd/eraseme 8b99978 1/4: Expunge CURRENCY_UNIT_IS_CENTS |
Date: |
Fri, 5 Mar 2021 14:08:06 -0500 (EST) |
branch: odd/eraseme
commit 8b999782d53e95fb2cd68ce4003ef2dac76b2aa3
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);