lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Numerics


From: Vadim Zeitlin
Subject: Re: [lmi] Numerics
Date: Sat, 14 May 2016 16:14:05 +0200

On Sat, 14 May 2016 10:01:30 +0000 Greg Chicares <address@hidden> wrote:

GC> On 2016-05-13 14:55, Vadim Zeitlin wrote:
GC> > 
GC> >  Very well. Then we must always pass it the rounding method. What bothers
GC> > me with this approach is the line
GC> > 
GC> >   currency x = 123.45;
GC> > 
GC> > It doesn't use any rounding method, yet you expect it to compile. How can
GC> > this work? The consistent thing to do here is to disallow such assignment
GC> > as well.
GC> 
GC> I think the implementation should behave, e.g., as if written like this:
GC> 
GC>   currency::operator=(double new_value)
GC>   {
GC>     double z = std::round(100 * new_value);
GC>     assert(materially_equal(new_value, 0.01 * z, some_epsilon));
GC>     cents_ = z;
GC>   }

 Exactly, yes, we need "some_epsilon" here.

GC> Then:
GC> 
GC>   currency x0 = 123.45;     // Okay.
GC>   currency x1 = 123.456789; // Throws.
GC> 
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.

 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).

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). 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.

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.

GC> Rounding is not necessarily inherent in a variable's type.

 Just to be clear: I consider that we have already established that we need
to specify the rounding type when creating a currency object from a double.
I realize that I was wrong about this initially and am convinced of the
need for the different rounding methods now.

GC> Let's see whether we can accommodate each other this way. I'm quite
GC> confident that we'll never need total_cents(). You aren't. So let's
GC> leave it in there, and rework lmi to use the new class.

 Very well, let me produce the new version of this class and see if it
comes closer to your expectations.

 Thanks,
VZ


reply via email to

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