Bug in NumberFormat() in 12th decimal place when using BigDecimals (and doubles!)

Hi guys! I just found and verified a very hard-to-notice bug with the NumberFormat() function when used on BigDecimal values with a lot of digits after the decimal (12 or more).

Lucee’s implementation of BigDecimal seems to allow up to 16 digits after the decimal point. If you use NumberFormat(), it’ll cause the 12th position to be one digit higher than it should be, and with certain values like 9 the error will propagate in spectacular fashion to places 1 thru 11, too. Run this in Lucee 6 (with “Precise Math” turned on in the admin) to see what I mean:

<cfset my_number = 0.1111111111111111>
<cfset my_formatted_number = NumberFormat(my_number,"0.0000000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number"><hr>

<cfset my_number = 0.9999999999999999>
<cfset my_formatted_number = NumberFormat(my_number,"0.0000000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number"><hr>

<cfset my_number = 0.4156616876960124>
<cfset my_formatted_number = NumberFormat(my_number,"0.0000000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number">

The formatted numbers output are these (run it yourself to verify):

0.1111111111121111 (2 not 1 in 12th position)
1.0000000000009999 (wow!)
0.4156616876970124 (7 not 6 in 12th position)

When not using BigDecimal and using double instead (i.e. in Lucee versions before 6, or in version 6 when “Precise Math” is turned off in the admin), Lucee supports up to 12 digits after the decimal point. Because this bug causes inaccuracy in the 12th digit, I suspect the NumberFormat() function was not properly modified to implement proper BigDecimal support.

Hope this helps! This really needs to be fixed ASAP due to the sneaky errors it can produce that are hard to notice! Thanks guys!

OS: Linux
Java Version: 11.0.11
Tomcat Version: 8.0.36
Lucee Version: 6.1.0.243

1 Like

Hey, it happens in the 12th position with “Precise Math” turned off, too.

I just turned it off, then made a version of the test code that only uses 12 places after the decimal, and the problem still happens:

<cfset my_number = 0.111111111111>
<cfset my_formatted_number = NumberFormat(my_number,"0.000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number"><hr>

<cfset my_number = 0.999999999999>
<cfset my_formatted_number = NumberFormat(my_number,"0.000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number"><hr>

<cfset my_number = 0.415661687696>
<cfset my_formatted_number = NumberFormat(my_number,"0.000000000000")>
<cfdump eval="my_number">
<cfdump eval="my_formatted_number">

The formatted numbers output are these (run it yourself to verify):

0.111111111112
1.000000000000
0.415661687697

So it even happens when using doubles, not just BigDecimals. Pretty serious bug that can cause hard-to-notice numeric inaccuracies. FYI.

1 Like

Important find.

1 Like

On further testing, I found it’s worse than I thought. It’s not just the 12th digit being wrongly increased by 1, there are other problems too. Check this out:

<cfset my_num = 9.3069229801124970>
<cfset my_num_formatted = NumberFormat(my_num,"0.0000000000000000")>

<cfdump eval="my_num">
<cfdump eval="my_num_formatted">

Output is:

9.306922980112497  (my_num)
9.3069229801134960  (my_num_formatted)

Notice that not only has decimal place 12 been wrongly increased by 1, but also decimal place 15 has been wrongly decreased by 1!

Someone really needs to fix this. NumberFormat() is very broken in these situations, and there’s no hard error so it’s very hard to notice, which means it can go undetected and give bad results for a long time, which is very bad for users.

Hi,

You can try on https://trycf.com/ but you will need to replace your cfdump eval by cfdump var.

Adobe Coldfusion 2023 has the same kind of issue. The results are not the same tho between Lucee and Adobe Coldfusion depending of the tests I did.

BoxLang look fine with this particular example (9.3069229801124970). We should look at what they are doing differently at this level.