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

Deviding by 3 #225

Closed kennyhyun closed 1 year ago

kennyhyun commented 1 year ago

I had never tried this package before, but I had today while I was curious about handling the notorious(?) 0.1 + 0.2 issue (https://0.30000000000000004.com/) in JS numbers (double precision floating point number).

Because it's from converting binary to decimal which leads infinite decimal.

To see how this package deals with the problem, I tried this

console.log(new decimal(1).div(3).mul(3).toString())

ended up with

"0.99999999999999999999"

but JS (v8) number does not have this issue

image

One possible weirdness would be

new decimal(1).div(3).mul(3) == new decimal(1)
// false
new decimal(1).div(3).mul(3).floor()
// 0

Could this be true and 1?

kennyhyun commented 1 year ago

BTW, python Decimal package has the same behaviour

Python 3.7.3 (default, Oct 31 2022, 14:04:00)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from decimal import *
>>> Decimal(1) / 3 * 3
Decimal('0.9999999999999999999999999999')
>>> 1/3*3
1.0
>>> Decimal(1) / 3 * 3 == Decimal(1)
False
MikeMcl commented 1 year ago

Division is performed to the current precision setting, so with Decimal.precision = 20, new Decimal(1).div(3) is exactly 0.33333333333333333333 which is not the same as 1/3.

If you want the result to equal 1 then you can just round the result using toSignificantDigits, toDecimalPlaces, round, toFixed etc.

Or you can just change the order of operations so you are multiplying the numerator:

new Decimal(1).div(3).mul(3).eq(1)     // false
new Decimal(1).mul(3).div(3).eq(1)     // true
kennyhyun commented 1 year ago

Thanks for the input

I would use .toSignificantDigits(decimal.precision-1).floor()

which is similar in native Number

image