Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

CF rounding issues

Enthusiast ,
Nov 16, 2023 Nov 16, 2023

This is an issue I have on CF2016, but I've tried CF Fiddle and it's the same result on all versions, so I presume it's not a bug.

 

I noticed that calculations were not rounding correctly, for example if the calculation gave a result of 7.875 and  I used NumberFormat to round it to two decimal places, I would have expected it to round up to 7.88

 

After a lot of playing around, and a few conversations with ChatGTP 🙂 I hacked around and found something unusual, if I change the value to a string, within the NumberFormat, it works.

 

Here is the code for the two tests with and without the ToString. Notice the different in rounding. Am I missing something? It gives me the right answer and I can't seem to break it, but it appears to be an odd way to go about things

 

<CFOUTPUT>

<b>Test 1</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding: <b>#NumberFormat(aff_payment,"9999999.99")#</b>

<br><br>

<b>Test 2</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding with ToString: <b>#NumberFormat(ToString(aff_payment),"9999999.99")#</b>

</CFOUTPUT>

 

Result

 

Test 1
Before rounding: 7.875
After NumberFormat rounding: 7.87

Test 2
Before rounding: 7.875
After NumberFormat rounding with ToString: 7.88

4.0K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 3 Correct answers

Community Expert , Apr 06, 2024 Apr 06, 2024

I ran the code, as below, on https://trycf.com 

 

<cfset aff_payment=7.875>

<cfoutput>
<b>Test 1</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding: <b>#NumberFormat(aff_payment,"9999999.99")#</b>

<br><br>

<b>Test 2</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding with ToString: <b>#NumberFormat(ToString(aff_payment),"9999999.99")#</b>

</cfoutput>

 

 

I used each of the following 10 CFML engines in turn:

BKBK_0-1712415420194.png

 

However, none of them reproduces the 7.

...
Translate
Enthusiast , Apr 10, 2024 Apr 10, 2024

I have to admit I can not remember what the outcome was at my side as it was almost 6 monhs ago. I searched threough the code I was working on and don't see me using the string approach. I also re-tried the code above (on CF2016) and it gave the right result. I have no idea what happened.

Translate
Community Expert , Jun 25, 2025 Jun 25, 2025

Paule, it's indeed not "new" behavior (though it may still bother many). For instance, it was discussed in a stackoverflow post 10 years ago.

 

And there it helpfully points out first that the REASON for what you see is that the fee ends up being a "double" datatype internally, and when you use the tostring it's converted of course to a "string". A few lines of code added to yours will show this, FWIW:

fee.getClass().getName(): #fee.getClass().getName()#<br>
<cfset fee_string=ToString(fee)>
fee_
...
Translate
Community Expert ,
Apr 06, 2024 Apr 06, 2024

I ran the code, as below, on https://trycf.com 

 

<cfset aff_payment=7.875>

<cfoutput>
<b>Test 1</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding: <b>#NumberFormat(aff_payment,"9999999.99")#</b>

<br><br>

<b>Test 2</b>
<br>
Before rounding: #aff_payment#
<br>
After NumberFormat rounding with ToString: <b>#NumberFormat(ToString(aff_payment),"9999999.99")#</b>

</cfoutput>

 

 

I used each of the following 10 CFML engines in turn:

BKBK_0-1712415420194.png

 

However, none of them reproduces the 7.87 result. In each case, the result is 7.88 for Test 1 and for Test 2.

 

BKBK_1-1712416035290.png

 

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 10, 2024 Apr 10, 2024

I have to admit I can not remember what the outcome was at my side as it was almost 6 monhs ago. I searched threough the code I was working on and don't see me using the string approach. I also re-tried the code above (on CF2016) and it gave the right result. I have no idea what happened.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 25, 2025 Jun 25, 2025

Resurrecting this old thread - I am encountering a similar problem with rounding.  Code example:

 

<!---
transaction amount is $10.00
add service fee of 2.25%
2.25% of 10.00 = 0.225
--->
<cfset transamount = 10.00>
<cfset fee = transamount * 0.0225>
<cfoutput>
The fee is #fee#<br>
Numeric value rounds to 2 decimal places incorrectly: #NumberFormat(fee,"999999.99")#<br>
Convert number to string, and it rounds correctly: #NumberFormat(ToString(fee),"999999.99")#
</cfoutput>

 

https://cffiddle.org/app/file?filepath=ca15b8d7-ec1d-4be0-b8be-d4aef69ef02c/6a6bdf29-2706-4b78-a0a0-...

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 25, 2025 Jun 25, 2025

Paule, it's indeed not "new" behavior (though it may still bother many). For instance, it was discussed in a stackoverflow post 10 years ago.

 

And there it helpfully points out first that the REASON for what you see is that the fee ends up being a "double" datatype internally, and when you use the tostring it's converted of course to a "string". A few lines of code added to yours will show this, FWIW:

fee.getClass().getName(): #fee.getClass().getName()#<br>
<cfset fee_string=ToString(fee)>
fee_string.getClass().getName(): #fee_string.getClass().getName()#<br>
ToString(fee): #fee_String# <br>

Granted, many will contend that "double" should be able to round correctly, but this is "what's happening".

 

And then as for how to "fix" it, it's that when you do the calculation, you can use precisionevaluate, as in:

<cfset fee = precisionevaluate(transamount * 0.0225)>

With that change alone, you will see your first output shows the .23 you expect. And you will see also that the code I offered above shows this to now be a BigDecimal type, which is what "solves" the rounding problem.

 

I realize "understanding" this may be a challenge (we don't get to know what's going on inside cf). But it's a single-line change, so seems worthwhile (assuming it has no ongoing negative consequences). Only you can determine that relative to any subsequent code using that fee variable.

 

Let us know what you think/how it goes for you.


/Charlie (troubleshooter, carehart. org)
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 26, 2025 Jun 26, 2025

Thanks for the tip, Charlie.  PrecisionEvaluate seems the way to go instead of leaving to chance that a ToString conversion gets it right.

 

BKBK, that's interesting about the different handling in Lucee vs. ColdFusion.  Good to know in case we ever migrate our code over to a Lucee platform.

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 26, 2025 Jun 26, 2025

If you were into adventure, you could make use of ColdFusion's "precision". In the example I gave earlier, you can see that the precision (that is, the amount by which the result falls short) is of the order of 2.7755... x 10^-17. This number consists of 16 zeros after the decimal point. 

 

So, you could compensate for that by adding 0.0000000000000001 to the result of any calaculation involving decimals. That appears as 1.0 x 10^-16 in the following example:

<cfset transamount = 10.00>

<!--- 1.0*10^-16 corrects for precision in ColdFusion --->
<cfset fee = transamount * 0.0225 + 1.0*10^-16>
<cfoutput>
The fee is #fee#<br>
Numeric value rounds to 2 decimal places: #NumberFormat(fee,"999999.99")#<br>
Convert number to string: #NumberFormat(ToString(fee),"999999.99")#
</cfoutput>

Of course, this approach only makes sense when the rounding-off accuracy you want is far fewer than 16 digits, Which is often the case in most practical scenarios.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 26, 2025 Jun 26, 2025
LATEST

BKBK, are you proposing that notion (of doing the math to manipulate the value) as another solution? Do you know if there's some value of it over using the simpler, built-in precisionevaluate function? Maybe it was more just for academic interest that you shared it. But if you (or anyone) may find it to produce a DIFFERENT result than the function, I'm sure some readers would be interested to hear that.


/Charlie (troubleshooter, carehart. org)
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 26, 2025 Jun 26, 2025

Glad to have helped. I realize you may not have wanted to mark my reply as an "answer", since yours was a comment in this previous thread by ACS in 2023. But since he marked as the answer his reply that he didn't know what solved it for him, it's ok to have a second reply marked as an answer--in case it may help others seeing this thread in the future.


/Charlie (troubleshooter, carehart. org)
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 26, 2025 Jun 26, 2025

The problem is not caused by rounding, nor by numberFormat(). It is caused by the way in which ColdFusion stores floating-point numbers. This differs from the way in which other CFML engines (such as Lucee, Box Lang, Railo) store such numbers.

 

As Charlie has shown, ColdFusion uses Double type when storing or manipulating decimals like fee. But using Double type loses precision. Which is why other languages like Lucee and Box Lang "promote" decimals to BigDecimal, which retains precision.

 

So, the variable "fee", which ColdFusion treats as a Double, will, as a result, lose precision by the time it enters numberFormat() as an argument. You can see it for yourself by running the following code:

<cfset fee=0.0225>

<!--- 
The operation 0.225-10*fee triggers a conversion to double, 
with loss of precision 
--->
<cfoutput>
fee = #fee#<br>
0.225-10*fee = #0.225-10*fee#<br>
</cfoutput>

The output:

floating_point.png

This is indeed an old issue in ColdFusion, as Charlie has said. In fact it came up again recently in the CFML Slack. The discussion there contains interesting background information.  

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources