lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master f0cdf93 3/6: Round halfway cases consistently


From: Greg Chicares
Subject: [lmi-commits] [lmi] master f0cdf93 3/6: Round halfway cases consistently to even
Date: Fri, 29 Jan 2021 15:44:15 -0500 (EST)

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

    Round halfway cases consistently to even
    
    The IEEE Standard for Floating-Point Arithmetic (IEEE 754) gives the
    rationale: rounding halfway to even avoids bias. Rounding away from zero
    is more expensive, because it's not implemented in silicon. Therefore,
    lmi avoids std::round(): harming speed in order to introduce (harmful)
    bias is not a favorable tradeoff.
    
    * ihs_basicval.cpp: In the example discussed inline, both std::round()
    and std::nearbyint() (with FE_TONEAREST) arrive at the same result:
      4.90001 --> 490
      4.89999 --> 490
    so this change does not affect the outcome.
---
 ihs_basicval.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index cba1bf0..bdc1cfa 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -56,7 +56,8 @@
 #include "value_cast.hpp"
 
 #include <algorithm>
-#include <cmath>                        // pow()
+#include <cfenv>                        // fesetround()
+#include <cmath>                        // nearbyint(), pow()
 #include <functional>                   // multiplies
 #include <limits>
 #include <numeric>
@@ -1495,18 +1496,17 @@ currency BasicValues::GetModalSpecAmtGSP(currency 
annualized_pmt) const
 /// typical practice) and rounded to integer to recapture the intended
 /// value.
 ///
-/// Of course, lmi would allow a table of corridor factors to have,
-/// say, seven decimal digits, so rounding might give an undesired
-/// answer even with a payment that exceeds the above example's 10^5
-/// by a factor of one plus epsilon. Until such amounts are stored as
-/// integral cents, this implementation cannot guarantee to give the
-/// desired answer in every case.
+/// This cannot be implemented as a single call to round_to() because
+/// it rounds twice: once to convert a floating-point corridor factor
+/// to an exact integer (4.9 --> 490 in the example above), and again
+/// after multiplying that integral factor by premium.
 
 currency BasicValues::GetModalSpecAmtCorridor(currency annualized_pmt) const
 {
     int const k = round_corridor_factor().decimals();
     double const s = nonstd::power(10, k);
-    double const z = std::round(s * GetCorridorFactor()[0]);
+    std::fesetround(FE_TONEAREST);
+    double const z = std::nearbyint(s * GetCorridorFactor()[0]);
     return round_min_specamt().c((z * annualized_pmt) / s);
 }
 



reply via email to

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