lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Numerics


From: Greg Chicares
Subject: Re: [lmi] Numerics
Date: Sat, 14 May 2016 20:47:49 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Icedove/38.6.0

On 2016-05-14 14:14, Vadim Zeitlin wrote:
> On Sat, 14 May 2016 10:01:30 +0000 Greg Chicares <address@hidden> wrote:
[...]
> GC> Now what if, say, crediting interest for a fractional period produces a
> GC> value like 123.456789 above, and we need to assign that value to a
> GC> currency object? We round it:
> GC> 
> GC>   currency x2 = round_somehow_to_whole_cents(123.456789); // Okay.
> GC>                       ^^^^^^^
> 
>  What does round_somehow_to_whole_cents() return? I assume it returns a
> "currency" object and in this case it must be either intimately coupled to
> the currency class (i.e. be its member method or a friend) or we must have
> some way to create a currency object from the exact amount of cents. And I
> strongly prefer the second approach.

I've been assuming something completely different: that it returns 'double'.

Let's consider an example from production code:

// Rounded interest increment.
double AccountValue::InterestCredited
    (double principal
    ,double monthly_rate
    ) const
{
    return round_interest_credit()(principal * ActualMonthlyRate(monthly_rate));
}
...
    // We may want to display credited interest separately.
    // Each interest increment is rounded separately.
    RegLnIntCred = InterestCredited(AVRegLn, YearsRegLnIntCredRate);
    PrfLnIntCred = InterestCredited(AVPrfLn, YearsPrfLnIntCredRate);

The result and the first argument represent currency amounts; the second
argument and ActualMonthlyRate() are interest rates, not currency. I'm
thinking that, at least as a first step, we'd change the type of the
'RegLnIntCred' and 'PrfLnIntCred' member variables to currency. Later,
if we wish, we might make InterestCredited() return currency.

The currency class doesn't need to know what sort of rounding has been
applied. If the value assigned here:
    RegLnIntCred = InterestCredited(AVRegLn, YearsRegLnIntCredRate);
has been rounded in any way consistent with the Currency concept (i.e.,
it is within some ε of 0.01 times an integer), then class currency
performs the assignment; otherwise, it throws.

Perhaps that's what you have in mind here:

> [...] or we must have
> some way to create a currency object from the exact amount of cents. And I
> strongly prefer the second approach.

What is the datatype of "the exact amount of cents"? I'm thinking
of it as a double that represents a currency amount, and has
accordingly already been rounded to a value that's almost equal
to 0.01 times an integer.

>  Also, do we really want to have many different functions for the different
> values of "somehow"? I'd rather have a single function taking the rounding
> method, e.g. round_currency_amount(double, rounding_style) (or,
> alternatively, a member from_value(double, rounding_style) method because I
> still don't see any material difference between the two).

I think 'rounding_rules.hpp' already does everything we need.

> GC> The "somehow" varies depending on context. In different contexts, we
> GC> might write various statements such as:
> GC> 
> GC>   currency x3 = round_up_to_whole_dollars(444.4444);
> GC>   currency x4 = round_down_to_whole_cents(555.5555);
> GC> 
> GC> The currency class remains the same regardless of context. The rounding
> GC> method varies by context. Those two concerns should be separated. That's
> GC> why I think rounding should be done independently, outside the currency
> GC> class.
> 
>  Very well, I can see the appeal of this (although in practice I still
> don't think it's going to matter much because the set of all possible
> rounding methods is quite finite and closed for extension).

Yet the set in 'rounding_rules.hpp' was extended just a few months ago:
  
http://svn.savannah.nongnu.org/viewvc/lmi/trunk/rounding_rules.hpp?root=lmi&r1=6081&r2=6414
and the meaning of any rounding rule can be changed at runtime in the
product editor.

> But, again,
> how do you propose implementing these rounding functions? For me the
> simplest and most natural way is to use the ctor from the total number of
> cents and pass it the result of the rounding.

In the production example discussed above, we keep using the existing
class rounding_rules.

> GC> Consider this example from above:
> GC>   currency x4 = round_down_to_whole_cents(555.5555);
> GC> We've obtained the value 555.5555 from some operation that produces
> GC> non-integral results. We round it in a specific way, producing, e.g.,
> GC>   555.5499999999, or
> GC>   555.5500000001
> GC> and now we want to assign that rounded value to a currency instance.
> GC> To do that, we must ascertain how many cents that floating-point value
> GC> is supposed to represent. That's the only problem before us. That is,
> GC> we don't need to decide what currency amount the original (unrounded)
> GC> 555.5555 means; we just need to determine what currency amount the
> GC> rounded value designates. IOW: we just rounded the value to cents, and
> GC> now we want to know how many cents we got. Surely we can answer that.
> 
>  Yes, but the problem is not at this level, you've just moved it one layer
> below. This round_down_to_whole_cents() must use some currency ctor and
> it's checking that we have an integral number of cents in this ctor that
> may be problematic. Perhaps you're right and we can just test for 1 ULP
> difference, but I'm not quite sure if it's always correct.

In the production example above, currency and rounding are orthogonal
concepts.

In theory, 1 ulp would be enough if lmi's rounding functions return
"correctly rounded" results. If not, there must be some ε that works.
Given a number of dollars like 123.4500000000001, we can determine
what integral number of cents is meant.




reply via email to

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