[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master d260b1a 2/3: Use currency type for DCV calcul
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master d260b1a 2/3: Use currency type for DCV calculations |
Date: |
Wed, 3 Mar 2021 17:25:13 -0500 (EST) |
branch: master
commit d260b1a51cb4cb25479582b119b56327343726aa
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Use currency type for DCV calculations
DCV may be rounded, though it need not be: it's an arbitrary choice.
Changed that choice: now DCV is rounded as AV is, largely because
currency calculations with intermediate rounding should be less likely
to produce minuscule regressions or to differ between 32- and 64-bit
builds.
---
account_value.hpp | 14 +++----
ihs_acctval.cpp | 14 +++----
ihs_avdebug.cpp | 2 +-
ihs_avmly.cpp | 121 +++++++++++++++++++++++++++++-------------------------
4 files changed, 80 insertions(+), 71 deletions(-)
diff --git a/account_value.hpp b/account_value.hpp
index 62e49a0..06ebf12 100644
--- a/account_value.hpp
+++ b/account_value.hpp
@@ -423,13 +423,13 @@ class LMI_SO AccountValue final
currency NecessaryPremium;
currency UnnecessaryPremium;
- // 7702A CVAT deemed cash value. CURRENCY !! not currency?
- double Dcv;
- double DcvDeathBft;
- double DcvNaar;
- double DcvCoiCharge;
- double DcvTermCharge;
- double DcvWpCharge;
+ // 7702A CVAT deemed cash value.
+ currency Dcv;
+ currency DcvDeathBft;
+ currency DcvNaar;
+ currency DcvCoiCharge;
+ currency DcvTermCharge;
+ currency DcvWpCharge;
// For other riders like AD&D, charge for DCV = charge otherwise.
// Honeymoon provision.
diff --git a/ihs_acctval.cpp b/ihs_acctval.cpp
index 5d0f0ff..88173a8 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -632,12 +632,12 @@ void AccountValue::SetInitialValues()
ItLapsed = false;
- Dcv = yare_input_.InforceDcv;
- DcvDeathBft = 0.0;
- DcvNaar = 0.0;
- DcvCoiCharge = 0.0;
- DcvTermCharge = 0.0;
- DcvWpCharge = 0.0;
+ Dcv = round_minutiae().c(yare_input_.InforceDcv);
+ DcvDeathBft = C0;
+ DcvNaar = C0;
+ DcvCoiCharge = C0;
+ DcvTermCharge = C0;
+ DcvWpCharge = C0;
HoneymoonActive = false;
// Identity element for std::max(), disregarding -INF and NaN.
@@ -1171,7 +1171,7 @@ void AccountValue::FinalizeYear()
VariantValues().CSVNet [Year] = dblize(csv_net);
VariantValues().CV7702 [Year] = dblize(cv_7702);
- InvariantValues().Dcv [Year] = Dcv;
+ InvariantValues().Dcv [Year] = dblize(Dcv);
// Update death benefit. 'DBReflectingCorr' currently equals the
// death benefit as of the beginning of the twelfth month, but its
diff --git a/ihs_avdebug.cpp b/ihs_avdebug.cpp
index 20be072..866f78b 100644
--- a/ihs_avdebug.cpp
+++ b/ihs_avdebug.cpp
@@ -476,7 +476,7 @@ void AccountValue::DebugPrint()
SetMonthlyDetail
(e7702ADeemedCv
,Irc7702A_->DebugGetIsMatChg()
- ? Irc7702A_->DebugGetSavedDCV()
+ ? round_minutiae().c(Irc7702A_->DebugGetSavedDCV())
: Dcv
);
SetMonthlyDetail(e7702ANetMaxNecPm ,NetMaxNecessaryPremium );
diff --git a/ihs_avmly.cpp b/ihs_avmly.cpp
index 5c35409..e63ca57 100644
--- a/ihs_avmly.cpp
+++ b/ihs_avmly.cpp
@@ -115,10 +115,10 @@ void AccountValue::DoMonthDR()
TxTestGPT();
// TODO ?? TAXATION !! Doesn't this mean dumpins and 1035s get ignored?
- LMI_ASSERT(0.0 <= Dcv);
+ LMI_ASSERT(C0 <= Dcv);
// TAXATION !! Is it really useful to comment the arguments here?
Irc7702A_->UpdateBft7702A
- (Dcv
+ (dblize(Dcv)
,dblize(DBReflectingCorr + TermDB) // DB7702A
,dblize(OldDB) // prior_db_7702A
,DBReflectingCorr == DBIgnoringCorr
@@ -141,7 +141,7 @@ void AccountValue::DoMonthDR()
// exchanges, but now seems unnecessary because this
// assertion never fires:
// LMI_ASSERT(kludge_account_value == Dcv);
- kludge_account_value = round_minutiae().c(Dcv);
+ kludge_account_value = Dcv;
}
kludge_account_value = std::max
(HoneymoonValue
@@ -150,7 +150,7 @@ void AccountValue::DoMonthDR()
);
// TODO ?? TAXATION !! Use CashValueFor7702() instead?
double max_necessary_premium = Irc7702A_->MaxNecessaryPremium
- (Dcv
+ (dblize(Dcv)
,dblize(AnnualTargetPrem)
,YearsTotLoadTgtLowestPremtax
,YearsTotLoadExcLowestPremtax
@@ -160,7 +160,7 @@ void AccountValue::DoMonthDR()
// max_necessary_premium = round_max_premium()(max_necessary_premium);
// CURRENCY !! already rounded by class Irc7702A--appropriately?
double max_non_mec_premium = Irc7702A_->MaxNonMecPremium
- (Dcv
+ (dblize(Dcv)
,dblize(AnnualTargetPrem)
,YearsTotLoadTgtLowestPremtax
,YearsTotLoadExcLowestPremtax
@@ -187,7 +187,7 @@ void AccountValue::DoMonthDR()
if(0 == Month)
{
Irc7702A_->UpdatePmt7702A
- (Dcv
+ (dblize(Dcv)
,dblize(-NetWD) // TAXATION !! This should be gross, not net.
,false
,dblize(AnnualTargetPrem)
@@ -232,13 +232,18 @@ void AccountValue::DoMonthDR()
// Material changes occurring on the same day (e.g. unnecessary
// premium triggering a corridor DB increase, depending on the 7702A
// interpretation chosen) are queued to be processed together.
+ {
+ // CURRENCY !! return modified value instead of altering argument
+ double z = dblize(Dcv);
Irc7702A_->RedressMatChg
- (Dcv // Potentially modified.
+ (z // Potentially modified.
,unnecessary_premium
,necessary_premium
,dblize(CashValueFor7702())
);
- LMI_ASSERT(0.0 <= Dcv);
+ Dcv = round_minutiae().c(z);
+ }
+ LMI_ASSERT(C0 <= Dcv);
TxRecognizePaymentFor7702A(UnnecessaryPremium, true);
TxAcceptPayment(UnnecessaryPremium);
@@ -600,15 +605,20 @@ void AccountValue::TxExch1035()
TxSetDeathBft();
TxSetTermAmt();
// TODO ?? TAXATION !! Should 1035 exchanges be handled somewhere else?
- LMI_ASSERT(0.0 == Dcv);
+ LMI_ASSERT(C0 == Dcv);
+ {
+ // CURRENCY !! return modified value instead of altering argument
+ double z = dblize(Dcv);
Irc7702A_->Update1035Exch7702A
- (Dcv
+ (z // Potentially modified.
,dblize(NetPmts[Month])
// TAXATION !! This assumes the term rider can be treated as death
benefit;
// use 'TermIsDbFor7702A'.
,dblize(ActualSpecAmt + TermSpecAmt)
// ,DBReflectingCorr + TermDB // TAXATION !! Alternate if 7702A benefit
is DB?
);
+ Dcv = round_minutiae().c(z);
+ }
if(HoneymoonActive)
{
@@ -626,7 +636,7 @@ void AccountValue::TxExch1035()
// Immediately after a 1035 exchange, DCV should be
// the 1035 amount reduced by any premium-based loads,
// but only for the current rate basis.
- LMI_ASSERT(materially_equal(Dcv, dblize(NetPmts[Month])));
+ LMI_ASSERT(Dcv == NetPmts[Month]);
// The initial seven-pay premium shown on the illustration
// must be its value immediately after any 1035 exchange,
@@ -1358,14 +1368,14 @@ void AccountValue::TxRecognizePaymentFor7702A
, kludge_account_value
+ GetRefundableSalesLoad()
);
- LMI_ASSERT(0.0 <= Dcv);
+ LMI_ASSERT(C0 <= Dcv);
// TODO ?? TAXATION !! Not correct yet--need to test pmt less deductible
WD; and
// shouldn't we deduct the *gross* WD? [Yes, if any fee is part of the
// WD, which it normally is.]
currency amount_paid_7702A = a_pmt;
Irc7702A_->UpdatePmt7702A
- (Dcv
+ (dblize(Dcv)
,dblize(amount_paid_7702A)
,a_this_payment_is_unnecessary
,dblize(AnnualTargetPrem)
@@ -1399,8 +1409,8 @@ void AccountValue::TxAcceptPayment(currency a_pmt)
process_payment(net_pmt);
- Dcv += std::max(0.0, dblize(net_pmt));
- LMI_ASSERT(0.0 <= Dcv);
+ Dcv += std::max(C0, net_pmt);
+ LMI_ASSERT(C0 <= Dcv);
if(HoneymoonActive)
{
@@ -1594,8 +1604,8 @@ void AccountValue::TxSetBOMAV()
process_deduction(MonthsPolicyFees + SpecAmtLoad);
- Dcv -= dblize(MonthsPolicyFees + SpecAmtLoad);
- Dcv = std::max(0.0, Dcv);
+ Dcv -= MonthsPolicyFees + SpecAmtLoad;
+ Dcv = std::max(C0, Dcv);
}
/// Set death benefit reflecting corridor and death benefit option.
@@ -1626,7 +1636,7 @@ void AccountValue::TxSetBOMAV()
/// currency prior_sa_7702A = ActualSpecAmt;
/// toward the beginning, and:
/// Irc7702A_->UpdateBft7702A(...);
-/// LMI_ASSERT(0.0 <= Dcv);
+/// LMI_ASSERT(C0 <= Dcv);
/// toward the end.
void AccountValue::TxSetDeathBft()
@@ -1691,12 +1701,13 @@ void AccountValue::TxSetDeathBft()
DB7702A = DBReflectingCorr + TermDB;
DcvDeathBft = std::max
- ( dblize(DBIgnoringCorr)
- , (
+ ( DBIgnoringCorr
+ , round_death_benefit().c
+ (
YearsCorridorFactor
* ( Dcv
- - dblize(std::min(C0, SurrChg()))
- + dblize(GetRefundableSalesLoad())
+ - std::min(C0, SurrChg())
+ + GetRefundableSalesLoad()
)
)
);
@@ -1784,7 +1795,7 @@ void AccountValue::TxSetCoiCharge()
// the account value by deducting a negative mortality charge.
#if defined USE_CURRENCY_CLASS
NAAR = round_naar().c
- ( DBReflectingCorr * DBDiscountRate[Year]
+ ( DBReflectingCorr * DBDiscountRate[Year]
- dblize(std::max(C0, TotalAccountValue()))
);
NAAR = std::max(C0, NAAR);
@@ -1801,12 +1812,11 @@ void AccountValue::TxSetCoiCharge()
// process_distribution(naar_forceout);
// TAXATION !! Should this be handled at the same time as GPT forceouts?
- DcvNaar = material_difference
- (std::max(DcvDeathBft, dblize(DBIgnoringCorr)) * DBDiscountRate[Year]
- ,std::max(0.0, Dcv)
+ DcvNaar = round_naar().c
+ ( std::max(DcvDeathBft, DBIgnoringCorr) * DBDiscountRate[Year]
+ - dblize(std::max(C0, Dcv))
);
- // DCV need not be rounded.
- DcvNaar = std::max(0.0, DcvNaar);
+ DcvNaar = std::max(C0, DcvNaar);
double coi_rate = GetBandedCoiRates(GenBasis_, ActualSpecAmt)[Year];
ActualCoiRate = coi_rate;
@@ -1814,8 +1824,7 @@ void AccountValue::TxSetCoiCharge()
CoiCharge = round_coi_charge().c(NAAR * ActualCoiRate);
YearsTotalCoiCharge += CoiCharge;
- // DCV need not be rounded.
- DcvCoiCharge = DcvNaar * YearsDcvCoiRate;
+ DcvCoiCharge = round_coi_charge().c(DcvNaar * YearsDcvCoiRate);
}
/// Calculate rider charges.
@@ -1847,7 +1856,7 @@ void AccountValue::TxSetRiderDed()
}
TermCharge = C0;
- DcvTermCharge = 0.0;
+ DcvTermCharge = C0;
if(TermRiderActive)
{
TermCharge = round_rider_charges().c
@@ -1857,11 +1866,13 @@ void AccountValue::TxSetRiderDed()
// it can't go into the corridor under tax assumptions.
// TAXATION !! Use a distinct discount rate for taxation? Or
// the policy's rate, as used for DcvNaar?
- DcvTermCharge = YearsDcvCoiRate * TermDB * DBDiscountRate[Year];
+ DcvTermCharge = round_rider_charges().c
+ (YearsDcvCoiRate * TermDB * DBDiscountRate[Year]
+ );
}
WpCharge = C0;
- DcvWpCharge = 0.0;
+ DcvWpCharge = C0;
if(yare_input_.WaiverOfPremiumBenefit)
{
switch(WaiverChargeMethod)
@@ -1871,7 +1882,7 @@ void AccountValue::TxSetRiderDed()
WpCharge = round_rider_charges().c
(YearsWpRate * std::min(ActualSpecAmt, WpLimit)
);
- DcvWpCharge = dblize(WpCharge);
+ DcvWpCharge = WpCharge;
}
break;
case oe_waiver_times_deductions:
@@ -1879,8 +1890,7 @@ void AccountValue::TxSetRiderDed()
// Premium load and M&E charges are not waived.
// The amount waived is subject to no maximum.
WpCharge = round_rider_charges().c
- (
- YearsWpRate
+ ( YearsWpRate
* (
CoiCharge
+ MonthsPolicyFees
@@ -1891,19 +1901,18 @@ void AccountValue::TxSetRiderDed()
+ TermCharge
)
);
- DcvWpCharge =
- YearsWpRate
+ DcvWpCharge = round_rider_charges().c
+ ( YearsWpRate
* (
DcvCoiCharge
- + dblize
- ( MonthsPolicyFees
- + SpecAmtLoad
- + AdbCharge
- + SpouseRiderCharge
- + ChildRiderCharge
- )
+ + MonthsPolicyFees
+ + SpecAmtLoad
+ + AdbCharge
+ + SpouseRiderCharge
+ + ChildRiderCharge
+ DcvTermCharge
- );
+ )
+ );
}
break;
}
@@ -1927,9 +1936,9 @@ void AccountValue::TxDoMlyDed()
+ ChildRiderCharge
;
- double dcv_mly_ded =
+ currency dcv_mly_ded =
DcvCoiCharge
- + dblize(simple_rider_charges)
+ + simple_rider_charges
+ DcvTermCharge
+ DcvWpCharge
;
@@ -1942,7 +1951,7 @@ void AccountValue::TxDoMlyDed()
process_deduction(MlyDed);
Dcv -= dcv_mly_ded;
- Dcv = std::max(0.0, Dcv);
+ Dcv = std::max(C0, Dcv);
// Policy and issue fees and the specified-amount load are really
// part of the monthly deduction, yet they must be kept distinct
@@ -2051,8 +2060,8 @@ void AccountValue::TxTakeSepAcctLoad()
// CURRENCY !! Does this seem right? Mightn't it take a sepacct load from
the genacct?
process_deduction(SepAcctLoad);
YearsTotalSepAcctLoad += SepAcctLoad;
- Dcv -= dblize(SepAcctLoad);
- Dcv = std::max(0.0, Dcv);
+ Dcv -= SepAcctLoad;
+ Dcv = std::max(C0, Dcv);
}
//============================================================================
@@ -2202,10 +2211,10 @@ void AccountValue::TxCreditInt()
GenAcctIntCred = C0;
}
- LMI_ASSERT(0.0 <= Dcv);
- if(0.0 < Dcv)
+ LMI_ASSERT(C0 <= Dcv);
+ if(C0 < Dcv)
{
- Dcv *= 1.0 + YearsDcvIntRate;
+ Dcv += round_interest_credit().c(YearsDcvIntRate * Dcv);
}
if(HoneymoonActive)
@@ -2524,8 +2533,8 @@ void AccountValue::TxTakeWD()
GrossWD += round_withdrawal().c(partial_surrchg);
process_distribution(GrossWD);
- Dcv -= dblize(GrossWD);
- Dcv = std::max(0.0, Dcv);
+ Dcv -= GrossWD;
+ Dcv = std::max(C0, Dcv);
switch(YearsDBOpt)
{