lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 3f69b748 1/4: Prefer std::uint64_t to std::in


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 3f69b748 1/4: Prefer std::uint64_t to std::int64_t where appropriate
Date: Fri, 13 May 2022 05:10:13 -0400 (EDT)

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

    Prefer std::uint64_t to std::int64_t where appropriate
    
    Limits need to be recognized and respected in rate_times_currency(),
    because, as currently written, it is subject to undefined behavior.
    Using an unsigned type doubles the limit, and is appropriate because
    both multiplicands are already asserted to be nonnegative.
    
    The purpose of this commit is *not* to replace undefined (mis)behavior
    (signed-integer overflow) with well-defined (mis)behavior (unsigned
    modulo arithmetic). Both give answers that are wrong in the problem
    domain, so neither is wanted; avoidance is left to future commits.
    
    Curiously, this commit causes several regressions in the "guaranteed
    premium" required by the NAIC illustration reg. That calculation is a
    "solve", i.e., a root-finding exercise, and its objective function is
    well known to have multiple roots close together. The regressions are
    plus or minus a cent or two, consistent with the pattern observed in
    the past. One might expect that replacing one integer type with
    another would induce no such change, but the universe is infinitely
    mysterious and one must cultivate a sense of wonder. Perhaps overflow
    is already occurring in early root-finding iterates, in different ways
    for signed and unsigned, causing eventual convergence to different but
    nearby roots that are just as valid.
---
 ul_utilities.cpp      | 16 ++++++++--------
 ul_utilities_test.cpp |  6 +++---
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/ul_utilities.cpp b/ul_utilities.cpp
index 83f76dbe..6a431277 100644
--- a/ul_utilities.cpp
+++ b/ul_utilities.cpp
@@ -32,7 +32,7 @@
 #include <algorithm>                    // generate(), min()
 #include <cfenv>                        // fesetround()
 #include <cmath>                        // nearbyint(), pow()
-#include <cstdint>                      // int64_t
+#include <cstdint>                      // uint64_t
 #include <numeric>                      // inner_product()
 #include <vector>
 
@@ -101,7 +101,7 @@ currency rate_times_currency
     ,round_to<double> const& rounder
     )
 {
-    using int64 = std::int64_t;
+    using uint64 = std::uint64_t;
 
     // Assume premium-rate argument is precise to at most eight decimals,
     // any further digits being representation error.
@@ -117,7 +117,7 @@ currency rate_times_currency
     // Store the result as a wide integer, to be used in integer math.
     // Use bourn_cast<>() for conversions here and elsewhere: it
     // implicitly asserts that values are preserved.
-    int64 irate = bourn_cast<int64>(std::nearbyint(rate * radix));
+    uint64 irate = bourn_cast<uint64>(std::nearbyint(rate * radix));
     // If the rate really has more than eight significant (non-erroneous)
     // digits, then treat them all as significant. In that case, there
     // is no representation error to be removed. The accompanying unit
@@ -143,7 +143,7 @@ currency rate_times_currency
 #endif // 0
     // Multiply integer rate by integral-cents amount.
     // Use a large integer type to avoid overflow.
-    int64 iprod = irate * bourn_cast<int64>(amount.cents());
+    uint64 iprod = irate * bourn_cast<uint64>(amount.cents());
     // Result is an integer--safe to represent as double now.
     // Function from_cents() has its own value-preservation test.
     currency cprod = from_cents(bourn_cast<double>(iprod));
@@ -153,8 +153,8 @@ currency rate_times_currency
     // result. However, if the remainder of integer division is zero,
     // then the result is exact, in which case the corresponding
     // rounded floating-point division may give the wrong answer.
-    int64 quotient  = iprod / radix;
-    int64 remainder = iprod % radix;
+    uint64 quotient  = iprod / radix;
+    uint64 remainder = iprod % radix;
     return
         ((0 == remainder)
         ? from_cents(bourn_cast<double>(quotient))
@@ -169,7 +169,7 @@ currency max_modal_premium
     ,round_to<double> const& rounder
     )
 {
-    using int64 = std::int64_t;
+    using uint64 = std::uint64_t;
 
     currency const annual_premium = rate_times_currency(rate, specamt, 
rounder);
     // Calculate modal premium from annual as a separate step,
@@ -181,6 +181,6 @@ currency max_modal_premium
     // premium is 12.30, then the monthly maximum is 1.02,
     // which is the highest level premium that can be paid twelve
     // times without exceeding the annual maximum: 12.24 <= 12.30 .
-    int64 annual_int = static_cast<int64>(annual_premium.cents());
+    uint64 annual_int = static_cast<uint64>(annual_premium.cents());
     return from_cents(bourn_cast<double>(annual_int / mode));
 }
diff --git a/ul_utilities_test.cpp b/ul_utilities_test.cpp
index 6c76340a..71a80a57 100644
--- a/ul_utilities_test.cpp
+++ b/ul_utilities_test.cpp
@@ -31,7 +31,7 @@
 #include <cfenv>                        // fesetround()
 #include <cfloat>                       // DECIMAL_DIG
 #include <cmath>                        // nearbyint()
-#include <cstdint>                      // int64_t
+#include <cstdint>                      // uint64_t
 
 void test_max_modal_premium()
 {
@@ -49,10 +49,10 @@ void test_max_modal_premium()
 
     auto test_excess_precision = [](double rate)
         {
-        using int64 = std::int64_t;
+        using uint64 = std::uint64_t;
         constexpr int radix {100'000'000};
         std::fesetround(FE_TONEAREST);
-        int64 irate = bourn_cast<int64>(std::nearbyint(rate * radix));
+        uint64 irate = bourn_cast<uint64>(std::nearbyint(rate * radix));
         return !materially_equal(bourn_cast<double>(irate), rate * radix);
         };
     LMI_TEST(!test_excess_precision(0.0                    ));



reply via email to

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