diff --git a/addons/product/_common.py b/addons/product/_common.py index f44f6b11a4c..c05dcee66a2 100644 --- a/addons/product/_common.py +++ b/addons/product/_common.py @@ -20,6 +20,9 @@ ############################################################################## from openerp import tools +import math + + def rounding(f, r): # TODO for trunk: log deprecation warning # _logger.warning("Deprecated rounding method, please use tools.float_round to round floats.") @@ -29,4 +32,4 @@ def rounding(f, r): def ceiling(f, r): if not r: return f - return tools.float_round(f, precision_rounding=r, rounding_method='UP') + return math.ceil(f / r) * r diff --git a/openerp/addons/base/test/base_test.yml b/openerp/addons/base/test/base_test.yml index 5a30bc22529..11f243592fa 100644 --- a/openerp/addons/base/test/base_test.yml +++ b/openerp/addons/base/test/base_test.yml @@ -198,8 +198,8 @@ - !python {model: res.currency}: | from tools import float_compare, float_is_zero, float_round, float_repr - def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr, rounding_method='HALF-UP'): - result = float_repr(float_round(amount, precision_digits=precision_digits, rounding_method=rounding_method), + def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr): + result = float_repr(float_round(amount, precision_digits=precision_digits), precision_digits=precision_digits) assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected) try_round(2.6745, '2.675') @@ -213,15 +213,6 @@ try_round(457.4554, '457.455') try_round(-457.4554, '-457.455') - # Try some rounding value with rounding method UP instead of HALF-UP - # We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives - # us 8175,0000000001234 as value, and if not handle correctly the rounding UP - # value will be incorrect (should be 8,175 and not 8,176) - try_round(8.175, '8.175', rounding_method='UP') - try_round(8.1751, '8.176', rounding_method='UP') - try_round(-8.175, '-8.175', rounding_method='UP') - try_round(-8.1751, '-8.175', rounding_method='UP') - # Extended float range test, inspired by Cloves Almeida's test on bug #882036. fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555] expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556'] diff --git a/openerp/tools/float_utils.py b/openerp/tools/float_utils.py index 79f5a34c7f6..5c934114e3c 100644 --- a/openerp/tools/float_utils.py +++ b/openerp/tools/float_utils.py @@ -29,11 +29,10 @@ 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, 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). +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. Precision must be given by ``precision_digits`` or ``precision_rounding``, not both! @@ -42,9 +41,6 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_ :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, @@ -56,7 +52,7 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_ # 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 (for normal rounding) + # TIE-BREAKING: HALF-UP # 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 @@ -70,19 +66,8 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_ normalized_value = value / rounding_factor # normalize epsilon_magnitude = math.log(abs(normalized_value), 2) epsilon = 2**(epsilon_magnitude-53) - 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 - + normalized_value += cmp(normalized_value,0) * epsilon + rounded_value = round(normalized_value) # round to integer result = rounded_value * rounding_factor # de-normalize return result