diff options
| author | Xavier Morel <xmo@odoo.com> | 2017-05-17 16:50:52 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@openerp.com> | 2017-08-19 02:34:20 +0200 |
| commit | 70599807a163d156f2399c56d698bcf1b876daf3 (patch) | |
| tree | a894b753fd811e701a99e6f4f3c589f8ccf1fba8 /odoo/tools/float_utils.py | |
| parent | b970bf865a7fa1dfbef0b8a365e39210fb5b75ce (diff) | |
[FIX] P3: round() compatibility shim
In Python 3 round:
* rounds half to even (banker's rounding) rather than up
* returns an int if not given a precision (or given a precision of
None)
* discards the negative sign when rounding to 0
Add a compatibility shim which implements P2 behaviour and at least
allows passing float_utils's tests.
Implementing float_round on top of Decimal was attempted but did not
work: although Decimal.quantize takes a non-integral
Decimal (e.g. ``d.quantize(D('0.001'))``) it only supports integral
powers of 10 and does not support quantizing to e.g. 0.05: while that
will not fail it will just use the exponent for quantization and 0.05
is thus equivalent to 0.01, which is not what we want if e.g. need to
round a value to 5 cents.
This is hinted at but maybe not clearly spelled out by the official
documentation:
> Return a value equal to the first operand after rounding
> and **having the exponent of** the second operand.
Also convert a bunch of hand-rolled currency rounding to just using
the round() method on res.currency.
Diffstat (limited to 'odoo/tools/float_utils.py')
| -rw-r--r-- | odoo/tools/float_utils.py | 17 |
1 files changed, 17 insertions, 0 deletions
diff --git a/odoo/tools/float_utils.py b/odoo/tools/float_utils.py index 9c37da07fe9..8f95a43ba6f 100644 --- a/odoo/tools/float_utils.py +++ b/odoo/tools/float_utils.py @@ -6,6 +6,23 @@ import math from odoo.tools import pycompat +if not pycompat.PY2: + import builtins + def round(f): + # P3's builtin round differs from P2 in the following manner: + # * it rounds half to even rather than up (away from 0) + # * round(-0.) loses the sign (it returns -0 rather than 0) + # * round(x) returns an int rather than a float + # + # this compatibility shim implements Python 2's round in terms of + # Python 3's so that important rounding error under P3 can be + # trivially fixed, assuming the P2 behaviour to be debugged and + # correct. + roundf = builtins.round(f) + if builtins.round(f + 1) - roundf != 1: + return f + math.copysign(0.5, f) + # copysign ensures round(-0.) -> -0 *and* result is a float + return math.copysign(roundf, f) def _float_check_precision(precision_digits=None, precision_rounding=None): assert (precision_digits is not None or precision_rounding is not None) and \ |
