Revert "[FIX] product,float_utils: perform ceiling via float_round with new rounding_method UP"
This reverts commit d4972ffdb6
.
Seems to break some cases, at least in _product_reserve from stock/stock.py
Actual use case:
SELECT product_uom, sum(product_qty) AS product_qty FROM stock_move WHERE location_dest_id=%s AND location_id<>%s AND product_id=3645 AND state='done' GROUP BY product_uom;
returning 1 | 6
SELECT product_uom,-sum(product_qty) AS product_qty FROM stock_move WHERE location_id=%s AND location_dest_id<>%s AND product_id=%s AND state in ('done', 'assigned') GROUP BY product_uom;
returning 1 | -6
results += cr.dictfetchall()
total = 0.0
results2 = 0.0
for r in results:
amount = uom_obj._compute_qty(cr, uid, r['product_uom'], r['product_qty'], context.get('uom', False))
results2 += amount
total += amount
Total = 1, amount = -5
It should actually be
Total = 0, amount = -6
This commit is contained in:
parent
ceff8ef899
commit
333852e19d
|
@ -20,6 +20,9 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from openerp import tools
|
from openerp import tools
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
def rounding(f, r):
|
def rounding(f, r):
|
||||||
# TODO for trunk: log deprecation warning
|
# TODO for trunk: log deprecation warning
|
||||||
# _logger.warning("Deprecated rounding method, please use tools.float_round to round floats.")
|
# _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):
|
def ceiling(f, r):
|
||||||
if not r:
|
if not r:
|
||||||
return f
|
return f
|
||||||
return tools.float_round(f, precision_rounding=r, rounding_method='UP')
|
return math.ceil(f / r) * r
|
||||||
|
|
|
@ -198,8 +198,8 @@
|
||||||
-
|
-
|
||||||
!python {model: res.currency}: |
|
!python {model: res.currency}: |
|
||||||
from tools import float_compare, float_is_zero, float_round, float_repr
|
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'):
|
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, rounding_method=rounding_method),
|
result = float_repr(float_round(amount, precision_digits=precision_digits),
|
||||||
precision_digits=precision_digits)
|
precision_digits=precision_digits)
|
||||||
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
|
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
|
||||||
try_round(2.6745, '2.675')
|
try_round(2.6745, '2.675')
|
||||||
|
@ -213,15 +213,6 @@
|
||||||
try_round(457.4554, '457.455')
|
try_round(457.4554, '457.455')
|
||||||
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.
|
# Extended float range test, inspired by Cloves Almeida's test on bug #882036.
|
||||||
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
|
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
|
||||||
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
|
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
|
||||||
|
|
|
@ -29,11 +29,10 @@ def _float_check_precision(precision_digits=None, precision_rounding=None):
|
||||||
return 10 ** -precision_digits
|
return 10 ** -precision_digits
|
||||||
return precision_rounding
|
return precision_rounding
|
||||||
|
|
||||||
def float_round(value, precision_digits=None, precision_rounding=None, rounding_method='HALF-UP'):
|
def float_round(value, precision_digits=None, precision_rounding=None):
|
||||||
"""Return ``value`` rounded to ``precision_digits`` decimal digits,
|
"""Return ``value`` rounded to ``precision_digits``
|
||||||
minimizing IEEE-754 floating point representation errors, and applying
|
decimal digits, minimizing IEEE-754 floating point representation
|
||||||
the tie-breaking rule selected with ``rounding_method``, by default
|
errors, and applying HALF-UP (away from zero) tie-breaking rule.
|
||||||
HALF-UP (away from zero).
|
|
||||||
Precision must be given by ``precision_digits`` or ``precision_rounding``,
|
Precision must be given by ``precision_digits`` or ``precision_rounding``,
|
||||||
not both!
|
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
|
:param float precision_rounding: decimal number representing the minimum
|
||||||
non-zero value at the desired precision (for example, 0.01 for a
|
non-zero value at the desired precision (for example, 0.01 for a
|
||||||
2-digit precision).
|
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
|
:return: rounded float
|
||||||
"""
|
"""
|
||||||
rounding_factor = _float_check_precision(precision_digits=precision_digits,
|
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
|
# 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
|
# 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.
|
# 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
|
# 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
|
# 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
|
normalized_value = value / rounding_factor # normalize
|
||||||
epsilon_magnitude = math.log(abs(normalized_value), 2)
|
epsilon_magnitude = math.log(abs(normalized_value), 2)
|
||||||
epsilon = 2**(epsilon_magnitude-53)
|
epsilon = 2**(epsilon_magnitude-53)
|
||||||
if rounding_method == 'HALF-UP':
|
normalized_value += cmp(normalized_value,0) * epsilon
|
||||||
normalized_value += cmp(normalized_value,0) * epsilon
|
rounded_value = round(normalized_value) # round to integer
|
||||||
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
|
result = rounded_value * rounding_factor # de-normalize
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue