MikeMcl / decimal.js

An arbitrary-precision Decimal type for JavaScript
http://mikemcl.github.io/decimal.js
MIT License
6.35k stars 480 forks source link

Rounding precision #195

Closed ghost closed 2 years ago

ghost commented 2 years ago

Hello,

It seems I've found an issue with the rounding of numbers like 4.4249.

Currently, if I run Decimal(4.4249).toNearest('0.01', Decimal.ROUND_HALF_UP).toString(), I get 4.42.

I've tried with other functions like toSD, toDP, toPrecision, all gave me the same result.

Although, if I run Decimal(4.4249).toNearest('0.001', Decimal.ROUND_HALF_UP).toNearest('0.01', Decimal.ROUND_HALF_UP).toString(), I get 4.43.

I expected the rounding system to start from the last digit and apply rounding decimal by decimal up to the asked precision.

Is there a way to do that with decimal.js ?

MikeMcl commented 2 years ago
x = Decimal(4.4249).toNearest('0.001', Decimal.ROUND_HALF_UP);
console.log(`x: ${x}`);    // "4.425"
y = x.toNearest('0.01', Decimal.ROUND_HALF_UP).toString();
console.log(`y: ${y}`);    // "4.43" as expected

I expected the rounding system to start from the last digit and apply rounding decimal by decimal up to the asked precision. Is there a way to do that with decimal.js ?

Sorry, I am not clear what you mean. Please refer to your example and state which digit you think should or should not be rounded up. The behaviour in the example is as expected.

ghost commented 2 years ago
x = Decimal(4.4249).toNearest('0.001', Decimal.ROUND_HALF_UP);
console.log(`x: ${x}`);    // "4.425"
y = x.toNearest('0.01', Decimal.ROUND_HALF_UP).toString();
console.log(`y: ${y}`);    // "4.43" as expected

Indeed, if I apply toNearest to each decimal position, I obtain the "4.43" as expected.

Sorry, that isn't clear. Please refer to your example and state which digit you think should or should not be rounded up. The behaviour in the example is as expected.

I expected to get "4.43" with just one step:

x = Decimal(4.4249).toNearest('0.01', Decimal.ROUND_HALF_UP);
console.log(`x: ${x}`);    // "x: 4.42", I expected "x: 4.43"

It seems decimal.js truncates the number to 3 decimals and then apply the rounding to 2 decimals.

I expected it apply the rounding for each decimal position (without doing any truncation).

For example (with more digits to be more explicit), I'm looking for a method doing all these steps automatically to have the value of e:

a = Decimal(4.4249123).toNearest('0.000001', Decimal.ROUND_HALF_UP);
console.log(`a: ${a}`);    // "a: 4.424912"
b = a.toNearest('0.00001', Decimal.ROUND_HALF_UP);
console.log(`b: ${b}`);    // "b: 4.42491"
c = b.toNearest('0.0001', Decimal.ROUND_HALF_UP);
console.log(`c: ${c}`);    // "c: 4.4249"
d = c.toNearest('0.001', Decimal.ROUND_HALF_UP);
console.log(`d: ${d}`);    // "d: 4.425"
e = d.toNearest('0.01', Decimal.ROUND_HALF_UP);
console.log(`e: ${e}`);    // "e: 4.43"

If I read the API documentation correctly, there's no such method, isn't it ?

MikeMcl commented 2 years ago

There's no such method because rounding does not normally work that way.

If you want 4.424912 to be rounded to 4.43 you wouldn't use toNearest('0.01', Decimal.ROUND_HALF_UP) as the nearest digit in the second decimal place is clearly 2 not 3.

The following is an example of how to get 4.43

x = Decimal(4.4249).toDecimalPlaces(2, Decimal.ROUND_UP);
console.log(x);    // "4.43"

It seems decimal.js truncates the number to 3 decimals and then apply the rounding to 2 decimals.

It's not a matter of truncation, it's just that 4.4249 is nearer 4.42 then 4.43.

ghost commented 2 years ago

Thanks for your time, I better understand how decimal.js work with rounding.