[MERGE] forward port of branch saas-3 up to c89d1a0
This commit is contained in:
commit
562272d9a0
|
@ -168,7 +168,7 @@ class account_followup_print(osv.osv_memory):
|
||||||
if partner.max_followup_id.send_letter:
|
if partner.max_followup_id.send_letter:
|
||||||
partner_ids_to_print.append(partner.id)
|
partner_ids_to_print.append(partner.id)
|
||||||
nbprints += 1
|
nbprints += 1
|
||||||
message = _("Follow-up letter of ") + "<I> " + partner.partner_id.latest_followup_level_id_without_lit.name + "</I>" + _(" will be sent")
|
message = "%s<I> %s </I>%s" % (_("Follow-up letter of "), partner.partner_id.latest_followup_level_id_without_lit.name, _(" will be sent"))
|
||||||
partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context)
|
partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context)
|
||||||
if nbunknownmails == 0:
|
if nbunknownmails == 0:
|
||||||
resulttext += str(nbmails) + _(" email(s) sent")
|
resulttext += str(nbmails) + _(" email(s) sent")
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
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.")
|
||||||
|
@ -32,4 +29,4 @@ def rounding(f, r):
|
||||||
def ceiling(f, r):
|
def ceiling(f, r):
|
||||||
if not r:
|
if not r:
|
||||||
return f
|
return f
|
||||||
return math.ceil(f / r) * r
|
return tools.float_round(f, precision_rounding=r, rounding_method='UP')
|
||||||
|
|
|
@ -134,10 +134,10 @@ class product_uom(osv.osv):
|
||||||
'name': fields.char('Unit of Measure', required=True, translate=True),
|
'name': fields.char('Unit of Measure', required=True, translate=True),
|
||||||
'category_id': fields.many2one('product.uom.categ', 'Product Category', required=True, ondelete='cascade',
|
'category_id': fields.many2one('product.uom.categ', 'Product Category', required=True, ondelete='cascade',
|
||||||
help="Conversion between Units of Measure can only occur if they belong to the same category. The conversion will be made based on the ratios."),
|
help="Conversion between Units of Measure can only occur if they belong to the same category. The conversion will be made based on the ratios."),
|
||||||
'factor': fields.float('Ratio', required=True,digits=(12, 12),
|
'factor': fields.float('Ratio', required=True, digits=0, # force NUMERIC with unlimited precision
|
||||||
help='How much bigger or smaller this unit is compared to the reference Unit of Measure for this category:\n'\
|
help='How much bigger or smaller this unit is compared to the reference Unit of Measure for this category:\n'\
|
||||||
'1 * (reference unit) = ratio * (this unit)'),
|
'1 * (reference unit) = ratio * (this unit)'),
|
||||||
'factor_inv': fields.function(_factor_inv, digits=(12,12),
|
'factor_inv': fields.function(_factor_inv, digits=0, # force NUMERIC with unlimited precision
|
||||||
fnct_inv=_factor_inv_write,
|
fnct_inv=_factor_inv_write,
|
||||||
string='Bigger Ratio',
|
string='Bigger Ratio',
|
||||||
help='How many times this Unit of Measure is bigger than the reference Unit of Measure in this category:\n'\
|
help='How many times this Unit of Measure is bigger than the reference Unit of Measure in this category:\n'\
|
||||||
|
|
|
@ -592,8 +592,14 @@
|
||||||
<field name="uom_type" on_change="onchange_type(uom_type)"/>
|
<field name="uom_type" on_change="onchange_type(uom_type)"/>
|
||||||
<label for="factor"/>
|
<label for="factor"/>
|
||||||
<div>
|
<div>
|
||||||
<field name="factor" attrs="{'invisible':[('uom_type','!=','smaller')]}"/>
|
<field name="factor"
|
||||||
<field name="factor_inv" attrs="{'invisible':[('uom_type','!=','bigger')]}"/>
|
digits="[42,5]"
|
||||||
|
attrs="{'invisible':[('uom_type','!=','smaller')],
|
||||||
|
'readonly':[('uom_type','!=','smaller')]}"/>
|
||||||
|
<field name="factor_inv"
|
||||||
|
digits="[42,5]"
|
||||||
|
attrs="{'invisible':[('uom_type','!=','bigger')],
|
||||||
|
'readonly':[('uom_type','!=','bigger')]}"/>
|
||||||
<p attrs="{'invisible':[('uom_type','!=','smaller')]}" class="oe_grey">
|
<p attrs="{'invisible':[('uom_type','!=','smaller')]}" class="oe_grey">
|
||||||
e.g: 1 * (reference unit) = ratio * (this unit)
|
e.g: 1 * (reference unit) = ratio * (this unit)
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_project_project,project.project,model_project_project,project.group_project_user,1,0,0,0
|
access_project_project,project.project,model_project_project,project.group_project_user,1,0,0,0
|
||||||
access_project_project_manager,project.project,model_project_project,project.group_project_manager,1,1,1,1
|
access_project_project_manager,project.project,model_project_project,project.group_project_manager,1,1,1,1
|
||||||
access_account_analytic_account_user,account.analytic.account,analytic.model_account_analytic_account,project.group_project_user,1,0,0,0
|
access_account_analytic_account_user,account.analytic.account,analytic.model_account_analytic_account,project.group_project_user,1,0,0,0
|
||||||
access_account_analytic_account,account.analytic.account,analytic.model_account_analytic_account,project.group_project_manager,1,1,1,1
|
access_account_analytic_account,account.analytic.account,analytic.model_account_analytic_account,project.group_project_manager,1,1,1,1
|
||||||
access_project_task_type_user,project.task.type user,model_project_task_type,project.group_project_user,1,0,0,0
|
access_project_task_type_user,project.task.type user,model_project_task_type,project.group_project_user,1,0,0,0
|
||||||
access_project_task_type_manager,project.task.type manager,model_project_task_type,project.group_project_manager,1,1,1,1
|
access_project_task_type_manager,project.task.type manager,model_project_task_type,project.group_project_manager,1,1,1,1
|
||||||
access_project_task,project.task,model_project_task,project.group_project_user,1,1,1,1
|
access_project_task,project.task,model_project_task,project.group_project_user,1,1,1,1
|
||||||
access_project_task_work,project.task.work,model_project_task_work,project.group_project_user,1,1,1,1
|
access_project_task_work,project.task.work,model_project_task_work,project.group_project_user,1,1,1,1
|
||||||
access_report_project_task_user,report.project.task.user,model_report_project_task_user,project.group_project_manager,1,1,1,1
|
access_report_project_task_user,report.project.task.user,model_report_project_task_user,project.group_project_manager,1,1,1,1
|
||||||
access_partner_task user,base.res.partner user,base.model_res_partner,project.group_project_user,1,0,0,0
|
access_partner_task user,base.res.partner user,base.model_res_partner,project.group_project_user,1,0,0,0
|
||||||
access_task_on_partner,project.task on partners,model_project_task,base.group_user,1,0,0,0
|
access_task_on_partner,project.task on partners,model_project_task,base.group_user,1,0,0,0
|
||||||
access_project_on_partner,project.project on partners,model_project_project,base.group_user,1,0,0,0
|
access_project_on_partner,project.project on partners,model_project_project,base.group_user,1,0,0,0
|
||||||
access_project_task_sale_user,project.task salesman,model_project_task,base.group_sale_salesman,1,1,1,1
|
access_project_task_sale_user,project.task salesman,model_project_task,base.group_sale_salesman,1,1,1,1
|
||||||
access_project_task_type_sale_user,project.task.type salesman,project.model_project_task_type,base.group_sale_salesman,1,0,0,0
|
access_project_task_type_sale_user,project.task.type salesman,project.model_project_task_type,base.group_sale_salesman,1,0,0,0
|
||||||
access_project_task_history_sale_user,project.task.history salesman,project.model_project_task_history,base.group_sale_salesman,1,1,1,1
|
access_project_task_history_sale_user,project.task.history salesman,project.model_project_task_history,base.group_sale_salesman,1,1,1,1
|
||||||
access_project_project_sale_user,project.project salesman,model_project_project,base.group_sale_salesman,1,0,0,0
|
access_project_project_sale_user,project.project salesman,model_project_project,base.group_sale_salesman,1,0,0,0
|
||||||
access_account_analytic_line_project,account.analytic.line project,analytic.model_account_analytic_line,project.group_project_manager,1,1,1,1
|
access_account_analytic_line_project,account.analytic.line project,analytic.model_account_analytic_line,project.group_project_manager,1,1,1,1
|
||||||
access_project_task_history,project.task.history project,project.model_project_task_history,project.group_project_user,1,1,1,0
|
access_project_task_history,project.task.history project,project.model_project_task_history,project.group_project_user,1,1,1,0
|
||||||
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_manager,1,0,0,0
|
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_manager,1,0,0,0
|
||||||
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_user,1,0,0,0
|
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_user,1,0,0,0
|
||||||
access_resource_calendar,project.resource_calendar manager,resource.model_resource_calendar,project.group_project_user,1,0,0,0
|
access_resource_calendar,project.resource_calendar user,resource.model_resource_calendar,project.group_project_user,1,0,0,0
|
||||||
access_resource_calendar_leaves_user,resource.calendar.leaves user,resource.model_resource_calendar_leaves,project.group_project_user,1,1,1,1
|
access_resource_calendar_attendance,project.resource_calendar_attendance user,resource.model_resource_calendar_attendance,project.group_project_user,1,0,0,0
|
||||||
access_project_category,project.project_category,model_project_category,,1,0,0,0
|
access_resource_calendar_leaves_user,resource.calendar.leaves user,resource.model_resource_calendar_leaves,project.group_project_user,1,1,1,1
|
||||||
access_project_category_manager,project.project_category,model_project_category,project.group_project_manager,1,1,1,1
|
access_project_category,project.project_category,model_project_category,,1,0,0,0
|
||||||
access_mail_alias,mail.alias,mail.model_mail_alias,project.group_project_manager,1,1,1,1
|
access_project_category_manager,project.project_category,model_project_category,project.group_project_manager,1,1,1,1
|
||||||
|
access_mail_alias,mail.alias,mail.model_mail_alias,project.group_project_manager,1,1,1,1
|
||||||
|
|
|
|
@ -56,13 +56,13 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
|
||||||
|
|
||||||
return this.model.call('fields_get', []).then(function (f) {
|
return this.model.call('fields_get', []).then(function (f) {
|
||||||
self.fields = f;
|
self.fields = f;
|
||||||
self.fields.__count = {field:'__count', type: 'integer', string:_t('Quantity')};
|
self.fields.__count = {field:'__count', type: 'integer', string:_t('Count')};
|
||||||
self.groupby_fields = self.get_groupby_fields();
|
self.groupby_fields = self.get_groupby_fields();
|
||||||
self.measure_list = self.get_measures();
|
self.measure_list = self.get_measures();
|
||||||
self.add_measures_to_options();
|
self.add_measures_to_options();
|
||||||
self.pivot_options.row_groupby = self.create_field_values(self.pivot_options.row_groupby || []);
|
self.pivot_options.row_groupby = self.create_field_values(self.pivot_options.row_groupby || []);
|
||||||
self.pivot_options.col_groupby = self.create_field_values(self.pivot_options.col_groupby || []);
|
self.pivot_options.col_groupby = self.create_field_values(self.pivot_options.col_groupby || []);
|
||||||
self.pivot_options.measures = self.create_field_values(self.pivot_options.measures || [{field:'__count', type: 'integer', string:'Quantity'}]);
|
self.pivot_options.measures = self.create_field_values(self.pivot_options.measures || [{field:'__count', type: 'integer', string:'Count'}]);
|
||||||
self.pivot = new openerp.web_graph.PivotTable(self.model, self.domain, self.fields, self.pivot_options);
|
self.pivot = new openerp.web_graph.PivotTable(self.model, self.domain, self.fields, self.pivot_options);
|
||||||
self.pivot.update_data().then(function () {
|
self.pivot.update_data().then(function () {
|
||||||
self.display_data();
|
self.display_data();
|
||||||
|
|
|
@ -18,7 +18,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
|
||||||
this.updating = false;
|
this.updating = false;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.fields.__count = {type: 'integer', string:_t('Quantity')};
|
this.fields.__count = {type: 'integer', string:_t('Count')};
|
||||||
this.measures = options.measures || [];
|
this.measures = options.measures || [];
|
||||||
this.rows = { groupby: options.row_groupby, headers: null };
|
this.rows = { groupby: options.row_groupby, headers: null };
|
||||||
this.cols = { groupby: options.col_groupby, headers: null };
|
this.cols = { groupby: options.col_groupby, headers: null };
|
||||||
|
|
|
@ -1183,6 +1183,8 @@ class ir_actions_act_client(osv.osv):
|
||||||
|
|
||||||
def _get_params(self, cr, uid, ids, field_name, arg, context):
|
def _get_params(self, cr, uid, ids, field_name, arg, context):
|
||||||
result = {}
|
result = {}
|
||||||
|
# Need to remove bin_size from context, to obtains the binary and not the length.
|
||||||
|
context = dict(context, bin_size_params_store=False)
|
||||||
for record in self.browse(cr, uid, ids, context=context):
|
for record in self.browse(cr, uid, ids, context=context):
|
||||||
result[record.id] = record.params_store and eval(record.params_store, {'uid': uid}) or False
|
result[record.id] = record.params_store and eval(record.params_store, {'uid': uid}) or False
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -203,8 +203,8 @@
|
||||||
-
|
-
|
||||||
!python {model: res.currency}: |
|
!python {model: res.currency}: |
|
||||||
from openerp.tools import float_compare, float_is_zero, float_round, float_repr
|
from openerp.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):
|
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),
|
result = float_repr(float_round(amount, precision_digits=precision_digits, rounding_method=rounding_method),
|
||||||
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')
|
||||||
|
@ -218,6 +218,18 @@
|
||||||
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.176', rounding_method='UP')
|
||||||
|
try_round(-6.000, '-6.000', rounding_method='UP')
|
||||||
|
try_round(1.8, '2', 0, rounding_method='UP')
|
||||||
|
try_round(-1.8, '-2', 0, 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']
|
||||||
|
|
|
@ -178,7 +178,12 @@ def get_pg_type(f, type_override=None):
|
||||||
if field_type in FIELDS_TO_PGTYPES:
|
if field_type in FIELDS_TO_PGTYPES:
|
||||||
pg_type = (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type])
|
pg_type = (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type])
|
||||||
elif issubclass(field_type, fields.float):
|
elif issubclass(field_type, fields.float):
|
||||||
if f.digits:
|
# Explicit support for "falsy" digits (0, False) to indicate a
|
||||||
|
# NUMERIC field with no fixed precision. The values will be saved
|
||||||
|
# in the database with all significant digits.
|
||||||
|
# FLOAT8 type is still the default when there is no precision because
|
||||||
|
# it is faster for most operations (sums, etc.)
|
||||||
|
if f.digits is not None:
|
||||||
pg_type = ('numeric', 'NUMERIC')
|
pg_type = ('numeric', 'NUMERIC')
|
||||||
else:
|
else:
|
||||||
pg_type = ('float8', 'DOUBLE PRECISION')
|
pg_type = ('float8', 'DOUBLE PRECISION')
|
||||||
|
|
|
@ -91,7 +91,6 @@ class NumberedCanvas(canvas.Canvas):
|
||||||
self._saved_page_states = []
|
self._saved_page_states = []
|
||||||
|
|
||||||
def showPage(self):
|
def showPage(self):
|
||||||
self._saved_page_states.append(dict(self.__dict__))
|
|
||||||
self._startPage()
|
self._startPage()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
@ -126,6 +125,8 @@ class PageCount(platypus.Flowable):
|
||||||
|
|
||||||
class PageReset(platypus.Flowable):
|
class PageReset(platypus.Flowable):
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
"""Flag to close current story page numbering and prepare for the next
|
||||||
|
should be executed after the rendering of the full story"""
|
||||||
self.canv._doPageReset = True
|
self.canv._doPageReset = True
|
||||||
|
|
||||||
class _rml_styles(object,):
|
class _rml_styles(object,):
|
||||||
|
@ -936,6 +937,9 @@ class TinyDocTemplate(platypus.BaseDocTemplate):
|
||||||
self.handle_frameBegin()
|
self.handle_frameBegin()
|
||||||
|
|
||||||
def afterPage(self):
|
def afterPage(self):
|
||||||
|
if isinstance(self.canv, NumberedCanvas):
|
||||||
|
# save current page states before eventual reset
|
||||||
|
self.canv._saved_page_states.append(dict(self.canv.__dict__))
|
||||||
if self.canv._doPageReset:
|
if self.canv._doPageReset:
|
||||||
# Following a <pageReset/> tag:
|
# Following a <pageReset/> tag:
|
||||||
# - we reset page number to 0
|
# - we reset page number to 0
|
||||||
|
@ -1009,10 +1013,10 @@ class _rml_template(object):
|
||||||
story_cnt = 0
|
story_cnt = 0
|
||||||
for node_story in node_stories:
|
for node_story in node_stories:
|
||||||
if story_cnt > 0:
|
if story_cnt > 0:
|
||||||
# Reset Page Number with new story tag
|
|
||||||
fis.append(PageReset())
|
|
||||||
fis.append(platypus.PageBreak())
|
fis.append(platypus.PageBreak())
|
||||||
fis += r.render(node_story)
|
fis += r.render(node_story)
|
||||||
|
# end of story numbering computation
|
||||||
|
fis.append(PageReset())
|
||||||
story_cnt += 1
|
story_cnt += 1
|
||||||
try:
|
try:
|
||||||
if self.localcontext and self.localcontext.get('internal_header',False):
|
if self.localcontext and self.localcontext.get('internal_header',False):
|
||||||
|
|
|
@ -29,10 +29,11 @@ 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):
|
def float_round(value, precision_digits=None, precision_rounding=None, rounding_method='HALF-UP'):
|
||||||
"""Return ``value`` rounded to ``precision_digits``
|
"""Return ``value`` rounded to ``precision_digits`` decimal digits,
|
||||||
decimal digits, minimizing IEEE-754 floating point representation
|
minimizing IEEE-754 floating point representation errors, and applying
|
||||||
errors, and applying HALF-UP (away from zero) tie-breaking rule.
|
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``,
|
Precision must be given by ``precision_digits`` or ``precision_rounding``,
|
||||||
not both!
|
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
|
: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,
|
||||||
|
@ -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
|
# 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
|
# TIE-BREAKING: HALF-UP (for normal rounding)
|
||||||
# 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
|
||||||
|
@ -66,8 +70,23 @@ def float_round(value, precision_digits=None, precision_rounding=None):
|
||||||
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)
|
||||||
normalized_value += cmp(normalized_value,0) * epsilon
|
if rounding_method == 'HALF-UP':
|
||||||
rounded_value = round(normalized_value) # round to integer
|
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
|
||||||
|
# The math.ceil operation is applied on the absolute value in order to
|
||||||
|
# round "away from zero" and not "towards infinity", then the sign is
|
||||||
|
# restored.
|
||||||
|
|
||||||
|
elif rounding_method == 'UP':
|
||||||
|
sign = cmp(normalized_value, 0)
|
||||||
|
normalized_value -= sign*epsilon
|
||||||
|
rounded_value = math.ceil(abs(normalized_value))*sign # 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