[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);
}
- [lmi-commits] [lmi] master updated (6620be4 -> ef8f7b4), Greg Chicares, 2021/01/29
- [lmi-commits] [lmi] master 183844a 2/6: Round only when necessary, Greg Chicares, 2021/01/29
- [lmi-commits] [lmi] master f0cdf93 3/6: Round halfway cases consistently to even,
Greg Chicares <=
- [lmi-commits] [lmi] master 780be3c 1/6: Mark uncached product-file instances, Greg Chicares, 2021/01/29
- [lmi-commits] [lmi] master 417af8e 4/6: Simplify, Greg Chicares, 2021/01/29
- [lmi-commits] [lmi] master ef8f7b4 6/6: Record speed measurements, Greg Chicares, 2021/01/29
- [lmi-commits] [lmi] master 484ed01 5/6: Refine rounding of specamt strategies, Greg Chicares, 2021/01/29