diff options
| author | Cedric Snauwaert <csn@openerp.com> | 2014-09-23 17:39:14 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@openerp.com> | 2014-09-24 17:11:25 +0200 |
| commit | d4972ffdb6b9356a524eef1dbc11f455ff4473f2 (patch) | |
| tree | 157ec0170efdd29fb0da369dcaf2a32a8673f762 /openerp/tools/float_utils.py | |
| parent | 1933e926ff7457b6f116296190ec55eb908de9b3 (diff) | |
[FIX] product,float_utils: perform ceiling via float_round with new rounding_method UP
Modified product ceiling() to use float_round() with special mode
for rounding UP (away from zero), avoiding pathological cases where
float representations errors were ceiling to the superior unit.
Also added correspding tests for rounding_method=UP
Fixes issue #1125, and replaces PR #1126.
Diffstat (limited to 'openerp/tools/float_utils.py')
| -rw-r--r-- | openerp/tools/float_utils.py | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/openerp/tools/float_utils.py b/openerp/tools/float_utils.py index 5c934114e3c..79f5a34c7f6 100644 --- a/openerp/tools/float_utils.py +++ b/openerp/tools/float_utils.py @@ -29,10 +29,11 @@ def _float_check_precision(precision_digits=None, precision_rounding=None): return 10 ** -precision_digits return precision_rounding -def float_round(value, precision_digits=None, precision_rounding=None): - """Return ``value`` rounded to ``precision_digits`` - decimal digits, minimizing IEEE-754 floating point representation - errors, and applying HALF-UP (away from zero) tie-breaking rule. +def float_round(value, precision_digits=None, precision_rounding=None, rounding_method='HALF-UP'): + """Return ``value`` rounded to ``precision_digits`` decimal digits, + minimizing IEEE-754 floating point representation errors, and applying + the tie-breaking rule selected with ``rounding_method``, by default + HALF-UP (away from zero). Precision must be given by ``precision_digits`` or ``precision_rounding``, not both! @@ -41,6 +42,9 @@ def float_round(value, precision_digits=None, precision_rounding=None): :param float precision_rounding: decimal number representing the minimum non-zero value at the desired precision (for example, 0.01 for a 2-digit precision). + :param rounding_method: the rounding method used: 'HALF-UP' or 'UP', the first + one rounding up to the closest number with the rule that number>=0.5 is + rounded up to 1, and the latest one always rounding up. :return: rounded float """ rounding_factor = _float_check_precision(precision_digits=precision_digits, @@ -52,7 +56,7 @@ def float_round(value, precision_digits=None, precision_rounding=None): # we normalize the value before rounding it as an integer, and de-normalize # after rounding: e.g. float_round(1.3, precision_rounding=.5) == 1.5 - # TIE-BREAKING: HALF-UP + # TIE-BREAKING: HALF-UP (for normal rounding) # We want to apply HALF-UP tie-breaking rules, i.e. 0.5 rounds away from 0. # Due to IEE754 float/double representation limits, the approximation of the # real value may be slightly below the tie limit, resulting in an error of @@ -66,8 +70,19 @@ def float_round(value, precision_digits=None, precision_rounding=None): normalized_value = value / rounding_factor # normalize epsilon_magnitude = math.log(abs(normalized_value), 2) epsilon = 2**(epsilon_magnitude-53) - normalized_value += cmp(normalized_value,0) * epsilon - rounded_value = round(normalized_value) # round to integer + if rounding_method == 'HALF-UP': + normalized_value += cmp(normalized_value,0) * epsilon + rounded_value = round(normalized_value) # round to integer + + # TIE-BREAKING: UP (for ceiling operations) + # When rounding the value up, we instead subtract the epsilon value + # as the the approximation of the real value may be slightly *above* the + # tie limit, this would result in incorrectly rounding up to the next number + + elif rounding_method == 'UP': + normalized_value -= cmp(normalized_value,0) * epsilon + rounded_value = math.ceil(normalized_value) # ceil to integer + result = rounded_value * rounding_factor # de-normalize return result |
