lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] valyuta/005 7d6e748 02/17: Improve currency class an


From: Greg Chicares
Subject: [lmi-commits] [lmi] valyuta/005 7d6e748 02/17: Improve currency class and unit tests
Date: Sat, 16 Jan 2021 21:06:16 -0500 (EST)

branch: valyuta/005
commit 7d6e74896fd22c287987c9a03367cc93ac73ec70
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Improve currency class and unit tests
    
    Unary operator-() needn't be a member; a nonmember is preferable.
    
    Adding "const" to return types is an obsolete C++98-ism.
    
    Free-function operators defined in a header must be "inline".
---
 currency.hpp      | 16 +++++-----
 currency_test.cpp | 93 +++++++++++++++++++++++++++----------------------------
 2 files changed, 55 insertions(+), 54 deletions(-)

diff --git a/currency.hpp b/currency.hpp
index 9bc8df5..5f21c8a 100644
--- a/currency.hpp
+++ b/currency.hpp
@@ -46,9 +46,6 @@ class LMI_SO currency
     explicit currency(data_type z, raw_cents) :m_ {z} {}
 
     currency& operator+=(currency const& z) {m_ += z.m_; return *this;}
-// !! no--mutates (negates) *this
-//  currency const& operator-() {m_ = -m_; return *this;}
-    currency const operator-() const {return currency(-cents(), raw_cents {});}
 
     data_type cents() const {return m_;}
     // !! possible underflow?
@@ -58,24 +55,29 @@ class LMI_SO currency
     data_type m_;
 };
 
-bool operator==(currency const& lhs, currency const& rhs)
+inline currency operator-(currency const& z)
+{
+    return currency(-z.cents(), raw_cents {});
+}
+
+inline bool operator==(currency const& lhs, currency const& rhs)
 {
     return lhs.cents() == rhs.cents();
 }
 
-currency const operator+(currency const& lhs, currency const& rhs)
+inline currency operator+(currency const& lhs, currency const& rhs)
 {
     return currency {lhs} += rhs;
 }
 
-currency const operator-(currency const& lhs, currency const& rhs)
+inline currency operator-(currency const& lhs, currency const& rhs)
 {
     return currency {lhs} += -currency {rhs};
 }
 
 static inline currency const C0 {{}, raw_cents {}};
 
-std::ostream& operator<<(std::ostream& os, currency const& z)
+inline std::ostream& operator<<(std::ostream& os, currency const& z)
 {
     os << z.d(); return os;
 }
diff --git a/currency_test.cpp b/currency_test.cpp
index 8484530..e42aad6 100644
--- a/currency_test.cpp
+++ b/currency_test.cpp
@@ -27,10 +27,11 @@
 #include "test_tools.hpp"
 
 #include <limits>
+#include <sstream>
 
 namespace
 {
-    round_to<double> const round_to_cents(2, r_to_nearest);
+    round_to<double> const round_to_nearest_cent(2, r_to_nearest);
 } // Unnamed namespace.
 
 class currency_test
@@ -51,7 +52,6 @@ void currency_test::test_something()
 {
     // default ctor
     currency const a0;
-    std::cout << a0 << std::endl;
     BOOST_TEST(0.00 == a0.d());
     BOOST_TEST(   0 == a0.m_);
     // operator==()
@@ -63,9 +63,19 @@ void currency_test::test_something()
 //  currency a1(3.25);
 
     // explicit ctor
+    // 3.25 is an exact binary constant
     currency a1(325, raw_cents{});
-    BOOST_TEST_EQUAL(3.25, a1.d());
     BOOST_TEST_EQUAL( 325, a1.m_);
+    // cents()
+    BOOST_TEST_EQUAL( 325, a1.cents());
+    // d()
+    BOOST_TEST_EQUAL(3.25, a1.d());
+
+    // copy ctor
+    currency copy0 = a1;
+    BOOST_TEST_EQUAL( 325, copy0.m_);
+    currency copy1 {a1};
+    BOOST_TEST_EQUAL( 325, copy1.m_);
 
     // operator+=()
     a1 += a1;
@@ -74,6 +84,8 @@ void currency_test::test_something()
 
     // unary operator-()
     -a1;
+    // make sure that didn't mutate the object
+    // (making it a nonmember makes that mistake less likely)
     BOOST_TEST_EQUAL(6.50, a1.d());
     BOOST_TEST_EQUAL( 650, a1.m_);
     BOOST_TEST_EQUAL(-6.50, (-a1).d());
@@ -96,71 +108,58 @@ void currency_test::test_something()
     BOOST_TEST_EQUAL(-6.50, a2.d());
     BOOST_TEST_EQUAL( -650, a2.m_);
 
+    // operator<<()
+    // 1/64 is an exact binary constant, with so few digits that it
+    // must print with full precision by default
+    currency a3 {0.015625, raw_cents {}};
+    std::ostringstream oss;
+    oss << a3;
+    BOOST_TEST_EQUAL("0.00015625", oss.str());
+
     // round_to<>.c()
     double d0 = 123.99999999999;
-    currency c0 = round_to_cents.c(d0);
-std::cout << c0 << " converted from 123.999..." << std::endl;
+    currency c0 = round_to_nearest_cent.c(d0);
+    BOOST_TEST_EQUAL(12400, c0.m_);
     double d1 = 1.0 + std::numeric_limits<double>::epsilon();
-    currency c1 = round_to_cents.c(d1);
-std::cout << c1 << " converted from 1.0 + epsilon..." << std::endl;
+    currency c1 = round_to_nearest_cent.c(d1);
+    BOOST_TEST_EQUAL(100, c1.m_);
     double d2 = 1.0 - std::numeric_limits<double>::epsilon();
-    currency c2 = round_to_cents.c(d2);
-std::cout << c2 << " converted from 1.0 - epsilon..." << std::endl;
+    currency c2 = round_to_nearest_cent.c(d2);
+    BOOST_TEST_EQUAL(100, c2.m_);
 
-    // overflow?
+#if 0
+    // This is curious, but may not belong here.
+//  double big_num = nonstd::power(2.0, 53);
     double big_num = 1.0e100;
-#pragma GCC diagnostic ignored "-Wfloat-conversion"
-    currency::data_type big_int0 = big_num;
-std::cout << "int0: " << big_int0 << std::endl;
-    currency::data_type big_int1 = 1.0 * big_num;
-std::cout << "int1: " << big_int1 << std::endl;
-    currency::data_type big_int2 = 10.0 * big_num;
-std::cout << "int2: " << big_int2 << std::endl;
+    currency::data_type big_int1 =   1.0 * big_num;
+    BOOST_TEST_EQUAL(1.0e100, big_int1);
+    currency::data_type big_int2 =  10.0 * big_num;
+    BOOST_TEST_EQUAL(1.0e101, big_int2);
     currency::data_type big_int3 = 100.0 * big_num;
-std::cout << "int3: " << big_int3 << std::endl;
-    currency::data_type big_int4 = round(100.0 * big_num);
-std::cout << "int4: " << big_int4 << std::endl;
+    BOOST_TEST_EQUAL(1.0e102, big_int3);
+    round_to_nearest_cent.c(d0);
+    std::cout << std::fixed;
+std::cout << big_int3 << '\n' << 1.0e102 << '\n' << big_int3 - 1.0e102 << 
std::endl;
 
-#if defined CURRENCY_HAS_INTEGER_DATATYPE
     BOOST_TEST_THROW
-        (currency a3(big_num / 1000.0)
+        (round_to_nearest_cent.c(big_num / 1000.0)
         ,std::runtime_error
         ,"Cast would transgress upper limit."
         );
-//std::cout << a3 << std::endl;
-//std::cout << big_num << std::endl;
-//std::cout << "rounded: " << round(big_num) << std::endl;
 
     double too_big = std::numeric_limits<double>::max();
     BOOST_TEST_THROW
-        (currency a4(too_big)
-        ,std::runtime_error
-//      ,"Cast would transgress upper limit."
-        ,"Cannot cast infinite to integral."
-        );
-//    currency a4(too_big);
-//std::cout << a4 << std::endl;
-//std::cout << too_big << std::endl;
-//std::cout << "rounded: " << round(too_big) << std::endl;
-//std::cout << 100.0 * too_big << std::endl;
-
-    BOOST_TEST_THROW
-        (currency(bourn_cast<currency::data_type>(too_big), raw_cents{})
-        ,std::runtime_error
-        ,"Cast would transgress upper limit."
-        );
-    BOOST_TEST_THROW
-        (currency xyzzy(too_big)
+        (round_to_nearest_cent.c(too_big)
         ,std::runtime_error
 //      ,"Cast would transgress upper limit."
         ,"Cannot cast infinite to integral."
         );
-#endif // !defined CURRENCY_HAS_INTEGER_DATATYPE
+#endif // 0
 
     // quodlibet
-    currency b0 = round_to_cents.c(464.180000000000006821);
-    currency b1 = round_to_cents.c(263.01999999999998181);
-    currency b2 = round_to_cents.c(0.0);
+    currency b0 = round_to_nearest_cent.c(464.180000000000006821);
+    currency b1 = round_to_nearest_cent.c(263.01999999999998181);
+    currency b2 = round_to_nearest_cent.c(0.0);
     b2 += b0;
     b2 += b1;
     currency b3 = b0 + b1;



reply via email to

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