Comment 29 for bug 882036

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote : Re: [Bug 882036] Re: rounding error

Hello Cloves,

I was about to provide a similar comment:
your 2^-52 espilon seems to be to small (failed one -2.675) and
Pieter's (.1 / pow(10, p+2)) epsilon was to large: fails on r2(1.00499, 2)
I could test that 2^-51 would always work in the range I tested.

And I agree with you 2^-50 is probably small enough to be very safe to use
and large enough so it will always fix the rounding direction.
It would be cool if somebody has the courage to try a math model of that to
know what exactly the implications are (We had 17 digits of initial
precision, each time we sum or multiply 2 floats we loose that precision,
by doing that epsilon rounding trick, I guess we eat our precision even
more; at the end of the story it would be good to have orders of magnitudes
where we are safe with floats or not). I'm quite confident i can fit for
most SMB's even if I'm not qualified enough to defend the choice for floats.

Now if we should mimic HALF EVEN in decimal system, indeed, this is more
tricky to implement. I'm however surprised it could be mandatory (do the
cheap hand calculators one use have Half Even decimal rounding?).
In Brazil we have been emitting lot of fiscal electronic invoices computed
originally with OpenERP. Would the Fisc accept them (as they did) and then
bother us because rounding is not half even? For the Brazilian "Sped
fiscal" and "Sped Contavel" may be?
Also in France, one of our customers had a fiscal control this year and
they passed it despite this rounding behavior.

I'm not sure if the Decimal class based rounding is better. It seems better
of course but I'm not sure if it's worth the performance penalty, I'm
really not qualified from that.
I made a quick benchmark, so on my computer I have:

rounding 2.65 to 2 digits with
def roundf(f, p):
    return float(Decimal(str(f)).quantize(Decimal(str(pow(10,-p)))))
would take 47.34 seconds
where it takes 3.3 seconds with
def r1(f, p=0):
    return round(f + cmp(f, 0) * pow(2, -51), p)

So that's a fat 10x slower using Decimal rounding. If it can let you
control the rounding policy you want, I would favor Decimal rounding. But
again, I'm really not qualified here, so was just an opinion.

Comments are welcome.

On Wed, Nov 23, 2011 at 5:55 PM, Cloves Almeida <email address hidden> wrote:

> In Brazil it's the "ABNT NBR 5891" from 1977. A lot of government
> instructions refer to it (just google "NBR 5891 SEFAZ" or "NBR 5891
> BACEN"). It does not point to the HALF_EVEN but describe it.
>
> Interestingly, many sources assert that in US financial institutions
> also use the HALF_EVEN. In UK, Europe, US tax calculations, Euro
> conversions and general arithmetic use HALF_UP.
>
> BTW, (.1 / pow(10, p+2)) = pow(10,p+3) This is called "Machine
> Epsilon" according to some tests I did it can be assumed to be
> pow(2,-51) - my previous pow(2,-52) worked in 2.6 but failed in 2.7.
> 2^-50 is still a very low number and can be used safely. The link I
> provided earlier has some implementations for C++ that could be ported
> to Python (I haven't tested though)
>
>
> However, I still think
>
> def roundf(f, p):
> from decimal import *
> return float(Decimal(str(f)).quantize(Decimal(str(pow(10,-p)))))
>
> encapsulates the "evil decimal" and closes the issue. Later it could be
> optimized.
>
> In the future, a more accurate approach would be to allow the rounding
> to be specified per company.
>
>
> Em 23-11-2011 16:38, Raphaël Valyi - http://www.akretion.com escreveu:
> > Hello guys,
> >
> > just a word that I find it very interesting to try to recover from the
> > float lost precision at the rounding stage by looking for the shortest
> > decimal fraction that rounds correctly back to the true binary value. I
> > always spotted and insisted a few years ago that even with good coding
> (not
> > to be assumed too lightly) rounding would not behave like in a decimal
> > system and i would make OpenERP numbers awkward at best, but possibly
> > illegal if some law forces you to adopt some specific rounding technique.
> >
> > Going to decimal might be the solution. But If that can not be done
> > (probably not in 6.1), I think that re-defining a rounding function that
> > will round like a decimal system (assuming a limited precision) is
> > something interesting.
> >
> > I've been surprise o see that Python 2.7 would use the same technique
> now.
> > For instance if you write 0.1 in the interpreter it will write 0.1 while
> it
> > would have written 0.10000000000000001 in previous Python versions as it
> > was closer to the approximated binary value for 0.1.
> >
> > I've played a bit with the r2 method provided in
> > https://bugs.launchpad.net/openobject-server/+bug/882036/comments/19
> > def r2(f, p=0):
> > return round(f + cmp(f, 0.0) * (.1 / pow(10, p+2)), p)
> > Overall it's interesting but I could spot a bug: it has a very limited
> > precision.
> > If you try r2(1.00499, 2) you get 1.01 instead of 1.00
> >
> > After my very little test, you can increase the precision of it by
> putting
> > more then 2 in p+2, like:
> > def r2(f, p=0):
> > return round(f + cmp(f, 0.0) * (.1 / pow(10, p+5)), p)
> >
> > This increases the precision apparently. I didn't bother computing what
> > would be the maximal precision one could reach.
> >
> > Now some questions:
> > Do one of you know if that approach is totally flawed? If yes, could you
> > provide some counter example that given some limited entry precision
> would
> > fail to round a 2 digits like a decimal system?
> >
> > @Cloves, do you have some law text that says we should use
> ROUND_HALF_EVEN ?
> > I've always looked for that and never found anything. Even in France I
> > couldn't find such a text law (except for the old Franc / Euro
> conversion I
> > think; but hey it may become actual again ;-) )
> >
> >
> > Regards.
> >
> >
> > On Wed, Nov 23, 2011 at 2:52 PM, Ferdinand @ Camptocamp<
> > <email address hidden>> wrote:
> >
> >> may be this highlights the problem
> >> http://www.bytereef.org/mpdecimal/index.html
> >>
> >> --
> >> You received this bug notification because you are a member of OpenERP
> >> Committers, which is subscribed to OpenERP Server.
> >> https://bugs.launchpad.net/bugs/882036
> >>
> >> Title:
> >> rounding error
> >>
> >> Status in OpenERP Server:
> >> New
> >>
> >> Bug description:
> >> Concerns 6.0 and trunk.
> >> If you define a precision of 0.01, the rounding of 0.125 must be 0.13
> and
> >> not 0.12. The error is in the call of the format string "%.2f"%val which
> >> introduces a mathematical error. The round function must be called to
> apply
> >> the correct rounding before formatting the string. It should be:
> >> "%.2f"%round(val*100)/100
> >> Fix class digits_change of class float in the server. BUT fix also the
> >> gtk client AND web client as they all have that error (I let you find
> the
> >> right line)
> >>
> >> To manage notifications about this bug go to:
> >> https://bugs.launchpad.net/openobject-server/+bug/882036/+subscriptions
> >>
>
> --
> You received this bug notification because you are a member of OpenERP
> Committers, which is subscribed to OpenERP Server.
> https://bugs.launchpad.net/bugs/882036
>
> Title:
> rounding error
>
> Status in OpenERP Server:
> New
>
> Bug description:
> Concerns 6.0 and trunk.
> If you define a precision of 0.01, the rounding of 0.125 must be 0.13 and
> not 0.12. The error is in the call of the format string "%.2f"%val which
> introduces a mathematical error. The round function must be called to apply
> the correct rounding before formatting the string. It should be:
> "%.2f"%round(val*100)/100
> Fix class digits_change of class float in the server. BUT fix also the
> gtk client AND web client as they all have that error (I let you find the
> right line)
>
> To manage notifications about this bug go to:
> https://bugs.launchpad.net/openobject-server/+bug/882036/+subscriptions
>