Large decimal number strings lose precision when converted to a number format

Description

Converting a string containing a large decimal number (more than 15 digits) causes the number to be rounded and to lose all decimal places after the rounded digit.

In ACF, the decimal precision is preserved no matter how large (or small) the decimal value is.
in Lucee, adding a 14th character (15th if you count the decimal point) causes the value to round up, and all former precision is lost.

In summary, Lucee should respect the original casting / decimal precision when the variable was created in CFML or provided from an external source ( e.g. a JSON file ).

Example on TryCF:

Environment

None

Attachments

3

Activity

Pothys - MitrahSoft 5 October 2021 at 11:58
Edited

Michael Offner 17 September 2021 at 14:19

your example has nothing to do with the json functions

I’m not sure what you mean by that. My first example demonstrates the issue with de-serialization - specifically the loss of type once 11 decimal places is reached. The second example demonstrates the native handling as you switch from 10 to 11 decimal places. This was a known issue and, as noted above, we can probably work around that with explicit casting to ensure the outbound payload is accurate.

Problem is the lost of information happen BEFORE the json function is even called, as you can see in my code example, it is already 20 and 74 in the dump of the struct. The information in this examples already get lost in the compiler, when the compiler creates the literal number

Take this example

when look at the generated java code from this

you can see the number already is ConstantsDouble._12 in the bytecode, so everything that follows does not matter anymore.

BTW

With the new version that now looks as follows

so this Caster.toDouble(11.9999999999999) has no rounding anymore.

So in short all code following a literal number does not matter and does not reflect your issue.

Jon Clausen 17 September 2021 at 13:54

Thanks for the literal number clarification, when attempting to cast the big decimal.

As I said, we can change our code for the outbound, to workaround this limitation in the engine. Maintaining precision in the deserialization of JSON is now our blocker. Both of those stories have been rejected ( linked to this story ), however.

I will pull down the latest version of Lucee, since tryCF doesn’t have that and see what differences I see. Expect a report back by the EOD or Monday AM at the latest.

Michael Offner 17 September 2021 at 13:43

Let me address this first “as an end-user and developer that I’m still not understanding in the push-back from you on this story“.

I really try to help here, as you can see in the Lucee “6.0“ branch, i have completely rewritten number handling in Lucee 6 (4 days of work). I also have made changes to Lucee 5.3 today, based on your input and this ticket is in QA now, waiting for your input. i spend a LOT of time trying to explain what is going on, by taking all assumption out by backing everything i say with examples.

I try to get as much info as possible out of you, because your description did not match your code so far, if you see that as negative, i’m sorry. My goal is to help and solve the issue for you, as i said before, we cannot apply the change from Lucee 6 to 5, so we need a more specific approach and for that i need to completely understand what is going wrong. Best always is a 1 to 1 example that shows the problem and reduce assumptions to a absolute minimum.

Your example ALWAYS (also the last one) have literal numbers (a literal number is a number defined literally in the code), we know we have a limitation there (and i have addressed that limitation in the latest build), but i’m sure your problem is NOT with literal numbers, because you loads this number from a datasource, as you clarified in the last comment.

Also your last example uses literal numbers again

queryAddRow( qTest, [ createUUID(), bigDecimal.init( 19.9999999999999 ), bigDecimal.init( 73.9999999999999 ) ] );

but it should be

queryAddRow( qTest, [ createUUID(), bigDecimal.init( "19.9999999999999" ), bigDecimal.init( "73.9999999999999" ) ] );

then it works as you expect

Your example creates a literal number (double) and then passes that number to BigDecimal, that makes the BigDecimal useless, because you loose already the precision BEFORE you pass it in to BigDecimal

With the new Lucee 5.3 build i changed 2 things:

  1. literal numbers now get now not rounded at all as long they stay numbers (same rule still apply for to string conversion)

  2. i changed the rounding rules for the json (and WDDX,XML) converter, so it allows more digits (#.###############)

But i still think there is the option for a change in your code, that will solve the issue, without a Lucee version update necessary.

Jon Clausen 17 September 2021 at 13:05

There is one thing that, as an end-user and developer that I’m still not understanding in the push-back from you on this story:

I fully understanding java casting and why the numbers are being rounded, at the current time, when the right-hand assignment contains 11 decimal places.

What I don’t understand is why a workaround in 5.3.x can’t be implemented so that, if the right hand assignment is a decimal, and is greater than 10 places, why Lucee can’t auto-cast that as a BigDecimal. You have mentioned that this might be a regression, but I highly doubt there are downstream users who want their precision decimals to be rounded.

Fixed

Details

Assignee

Reporter

Priority

Labels

Fix versions

New Issue warning screen

Before you create a new Issue, please post to the mailing list first https://dev.lucee.org

Once the issue has been verified, one of the Lucee team will ask you to file an issue

Sprint

Created 9 August 2021 at 18:46
Updated 29 April 2022 at 15:00
Resolved 18 October 2021 at 09:05