Increase precision of math operations by using BigDecimal

Description

This ticket introduces BigDecimal usage all over BoxLang for two main use cases:

  • Being able to store and process very large numbers that wouldn't fit inside a Long or a Double like 111111111111111111111111111 + 222222222222222222222222222

  • Being able to retain precision for decimals-- even small ones.  For ex: (0.1 + 0.2).toString() outputs 0.30000000000000004 in Lucee 5 and ACF 2023.  But in Lucee 6 and BoxLang, it correctly returns .3 without any special work

So here is what has changed.  Not ALL numbers are a BigDecimal-- just the ones that need to be.

  • integer literals in your source code less than 11 chars will be a java Integer

  • integer literals in your source code less than 20 chars will be a java Long

  • All other integer literals in your source will be a java BigDecimal

  • All decimal literals in your source will be a java BigDecimal

When casting object to numbers internally, we will employ the same basic logic:

  • any recognizable classes like int, long, double, big decimal, etc are just returned directly

  • Any strings which contain integers (no decimal or sci notation) follow the same rules above (integer for small ones, long for bigger ones, big decimal for really big ones)

  • Any strings with a decimal or sci notation will be made a BigDecimal

Basically, we return the "Smallest" data type we can without losing any precision.  (An Integer is about 24 Bytes and a BigDecimal is about 64 Bytes so it seemed worth optimizing a bit)

Any BIF/UDF arg or component attribute using numeric or number as its type.

This means all BIFs using numeric can seamlessly handle huge numbers and high precision out of the box!

The last thing I did was modify ALL language numeric operators and numeric BIFs to add special handling for BigDecimals, if they are passed in, but still reserving a simpler, faster code path for smaller numbers so we can keep stuff like 1 + 1 nice and fast with little overhead.  Operations like Plus are also smart enough to detect that 1+1 can store its result in an integer but larger numbers need to "upgrade" their type so if you cross the threshold you seamlessly get the next "biggest" data type back. BoxLang code doesn’t not need to do ANYTHING special-- all math operations should “just work”. Only the internals of BoxLang needs to care about handing BigDecimals.

And on a more technical note, we're using the DECIMAL128 math context by default which follows the precision of the IEEE 754-2019 decimal128 format, 34 digits, and a rounding mode of HALF_EVEN.  People can modify the default BigDecimal handling in BoxLang by importing the MathUtil class and calling its methods. The change will apply to math operations right away.

Activity

Show:

John Whish August 9, 2024 at 6:52 AM

Great stuff! Thanks :)

Brad Wood August 8, 2024 at 5:47 PM

I’ve updated this to detail the changes I made that fix your other ticket and allow large numbers and high precision in general.

Fixed
Pinned fields
Click on the next to a field label to start pinning.

Details

Assignee

Reporter

Fix versions

Priority

Sentry

Created August 6, 2024 at 7:05 PM
Updated August 9, 2024 at 6:52 AM
Resolved August 7, 2024 at 5:09 AM