[FIX] web: rounding error
In JavaScript, the following computation introduces numerical errors: ``` Math.pow(10, -4) = 0.00009999999999999999 Math.pow(10, -5) = 0.000009999999999999999 ``` In the context of `round_decimals`, such errors propagate to `round_precision`, ultimately leading to inconsistencies between Python and JS (see #47300 for detailed use case). Instead of computing `Math.pow(10, decimals)`, we parse the scientific notation to prevent such inaccuracies. We also introduce tests in line with the Python tests. opw-2205092 closes odoo/odoo#47694 X-original-commit: cb471897bfa08d1fee6a9f49e90d9c120e1733fc Signed-off-by: Nicolas Martinelli (nim) <nim@odoo.com>
This commit is contained in:
@@ -571,7 +571,15 @@ var utils = {
|
||||
* @param {Number} decimals the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
|
||||
*/
|
||||
round_decimals: function (value, decimals) {
|
||||
return utils.round_precision(value, Math.pow(10,-decimals));
|
||||
/**
|
||||
* The following decimals introduce numerical errors:
|
||||
* Math.pow(10, -4) = 0.00009999999999999999
|
||||
* Math.pow(10, -5) = 0.000009999999999999999
|
||||
*
|
||||
* Such errors will propagate in round_precision and lead to inconsistencies between Python
|
||||
* and JavaScript. To avoid this, we parse the scientific notation.
|
||||
*/
|
||||
return utils.round_precision(value, parseFloat('1e' + -decimals));
|
||||
},
|
||||
/**
|
||||
* performs a half up rounding with arbitrary precision, correcting for float loss of precision
|
||||
|
||||
@@ -136,6 +136,66 @@ QUnit.module('core', {}, function () {
|
||||
assert.strictEqual(child.bar(), 'Child bar');
|
||||
})
|
||||
|
||||
QUnit.test('round_decimals', function (assert) {
|
||||
assert.expect(21);
|
||||
|
||||
var round_di = utils.round_decimals;
|
||||
|
||||
assert.strictEqual(String(round_di(1.0, 0)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 1)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 2)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 3)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 4)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 5)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 6)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 7)), '1');
|
||||
assert.strictEqual(String(round_di(1.0, 8)), '1');
|
||||
assert.strictEqual(String(round_di(0.5, 0)), '1');
|
||||
assert.strictEqual(String(round_di(-0.5, 0)), '-1');
|
||||
assert.strictEqual(String(round_di(2.6745, 3)), '2.6750000000000003');
|
||||
assert.strictEqual(String(round_di(-2.6745, 3)), '-2.6750000000000003');
|
||||
assert.strictEqual(String(round_di(2.6744, 3)), '2.674');
|
||||
assert.strictEqual(String(round_di(-2.6744, 3)), '-2.674');
|
||||
assert.strictEqual(String(round_di(0.0004, 3)), '0');
|
||||
assert.strictEqual(String(round_di(-0.0004, 3)), '0');
|
||||
assert.strictEqual(String(round_di(357.4555, 3)), '357.456');
|
||||
assert.strictEqual(String(round_di(-357.4555, 3)), '-357.456');
|
||||
assert.strictEqual(String(round_di(457.4554, 3)), '457.455');
|
||||
assert.strictEqual(String(round_di(-457.4554, 3)), '-457.455');
|
||||
});
|
||||
|
||||
QUnit.test('round_precision', function (assert) {
|
||||
assert.expect(26);
|
||||
|
||||
var round_pr = utils.round_precision;
|
||||
|
||||
assert.strictEqual(String(round_pr(1.0, 1)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.1)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.01)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.001)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.0001)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.00001)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.000001)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.0000001)), '1');
|
||||
assert.strictEqual(String(round_pr(1.0, 0.00000001)), '1');
|
||||
assert.strictEqual(String(round_pr(0.5, 1)), '1');
|
||||
assert.strictEqual(String(round_pr(-0.5, 1)), '-1');
|
||||
assert.strictEqual(String(round_pr(2.6745, 0.001)), '2.6750000000000003');
|
||||
assert.strictEqual(String(round_pr(-2.6745, 0.001)), '-2.6750000000000003');
|
||||
assert.strictEqual(String(round_pr(2.6744, 0.001)), '2.674');
|
||||
assert.strictEqual(String(round_pr(-2.6744, 0.001)), '-2.674');
|
||||
assert.strictEqual(String(round_pr(0.0004, 0.001)), '0');
|
||||
assert.strictEqual(String(round_pr(-0.0004, 0.001)), '0');
|
||||
assert.strictEqual(String(round_pr(357.4555, 0.001)), '357.456');
|
||||
assert.strictEqual(String(round_pr(-357.4555, 0.001)), '-357.456');
|
||||
assert.strictEqual(String(round_pr(457.4554, 0.001)), '457.455');
|
||||
assert.strictEqual(String(round_pr(-457.4554, 0.001)), '-457.455');
|
||||
assert.strictEqual(String(round_pr(-457.4554, 0.05)), '-457.45000000000005');
|
||||
assert.strictEqual(String(round_pr(457.444, 0.5)), '457.5');
|
||||
assert.strictEqual(String(round_pr(457.3, 5)), '455');
|
||||
assert.strictEqual(String(round_pr(457.5, 5)), '460');
|
||||
assert.strictEqual(String(round_pr(457.1, 3)), '456');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user