diff options
| author | Nicolas Martinelli <nim@odoo.com> | 2020-03-11 07:44:40 +0000 |
|---|---|---|
| committer | Nicolas Martinelli <nim@odoo.com> | 2020-03-16 12:01:57 +0000 |
| commit | a12479aaa8cb2c8311c8d6c0fceb0dc641b44eac (patch) | |
| tree | bb3aca9fcf4003f644feaecf64739c5fed341d80 | |
| parent | 0067aec0ddaf75d2608a6acc12cb26c08f70d12e (diff) | |
[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>
| -rw-r--r-- | addons/web/static/src/js/core/utils.js | 10 | ||||
| -rw-r--r-- | addons/web/static/tests/core/util_tests.js | 60 |
2 files changed, 69 insertions, 1 deletions
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index 699a912ccc3..93e9bda5593 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -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 diff --git a/addons/web/static/tests/core/util_tests.js b/addons/web/static/tests/core/util_tests.js index 3403c9effaf..96954e9d2f8 100644 --- a/addons/web/static/tests/core/util_tests.js +++ b/addons/web/static/tests/core/util_tests.js @@ -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'); + }); }); }); |
