summaryrefslogtreecommitdiff
path: root/odoo/tools
diff options
context:
space:
mode:
authorNicolas Martinelli <nim@odoo.com>2018-01-25 13:06:27 +0100
committerNicolas Martinelli <nim-odoo@users.noreply.github.com>2018-01-29 11:06:33 +0100
commit8ed7570b1cc50c55867f75eafb20c1deb89dbf5c (patch)
treef310adaf8c3f41a19275457b73f10b2c3998a85c /odoo/tools
parent43bf9ff29d1897405a575fa8643b7f80b5112c24 (diff)
[FIX] tools: float_round DOWN
The result of the following: ``` float_round(2.5, precision_rounding=0.05, rounding_method='DOWN') ``` is 2.45. Instead, 2.5 is expected. In case of the `DOWN` method, epsilon should be added, not subtracted. In the meantime, we introduce a slight reorganization of the method to make it clearer. opw-805008
Diffstat (limited to 'odoo/tools')
-rw-r--r--odoo/tools/float_utils.py27
1 files changed, 15 insertions, 12 deletions
diff --git a/odoo/tools/float_utils.py b/odoo/tools/float_utils.py
index bf3f0463c0a..3e13f9ee0bc 100644
--- a/odoo/tools/float_utils.py
+++ b/odoo/tools/float_utils.py
@@ -61,9 +61,6 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_
# In order to easily support rounding to arbitrary 'steps' (e.g. coin values),
# 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)
- # 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
# 1 unit in the last place (ulp) after rounding.
@@ -74,25 +71,31 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_
# Credit: discussion with OpenERP community members on bug 882036
normalized_value = value / rounding_factor # normalize
+ sign = math.copysign(1.0, normalized_value)
epsilon_magnitude = math.log(abs(normalized_value), 2)
epsilon = 2**(epsilon_magnitude-53)
- if rounding_method == 'HALF-UP':
- normalized_value += math.copysign(epsilon, normalized_value)
- rounded_value = round(normalized_value) # round to integer
- # TIE-BREAKING: UP (for ceiling[resp. flooring] operations)
- # When rounding the value up[resp. down], we instead subtract the epsilon value
+ # TIE-BREAKING: UP/DOWN (for ceiling[resp. flooring] operations)
+ # When rounding the value up[resp. down], we instead subtract[resp. add] 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[resp. down] to the next number
# The math.ceil[resp. math.floor] operation is applied on the absolute value in order to
# round "away from zero" and not "towards infinity", then the sign is
# restored.
- else:
- func = math.floor if rounding_method == 'DOWN' else math.ceil
- sign = math.copysign(1.0, normalized_value)
+ if rounding_method == 'UP':
normalized_value -= sign*epsilon
- rounded_value = func(abs(normalized_value)) * sign
+ rounded_value = math.ceil(abs(normalized_value)) * sign
+
+ elif rounding_method == 'DOWN':
+ normalized_value += sign*epsilon
+ rounded_value = math.floor(abs(normalized_value)) * sign
+
+ # TIE-BREAKING: HALF-UP (for normal rounding)
+ # We want to apply HALF-UP tie-breaking rules, i.e. 0.5 rounds away from 0.
+ else:
+ normalized_value += math.copysign(epsilon, normalized_value)
+ rounded_value = round(normalized_value) # round to integer
result = rounded_value * rounding_factor # de-normalize
return result