Skip to main content
Inspiring
November 16, 2023
Answered

CF rounding issues

  • November 16, 2023
  • 2 replies
  • 4158 views

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

    Correct answer Charlie Arehart

    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.

    2 replies

    Inspiring
    June 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-582474c4d699/d3842182-5110-4e88-9994-c775d21385ba.cfm

     

    Charlie Arehart
    Community Expert
    Charlie ArehartCommunity ExpertCorrect answer
    Community Expert
    June 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)
    Inspiring
    June 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.

     

    BKBK
    Community Expert
    Community Expert
    April 6, 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:

     

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

     

     

     

     

    ACS LLCAuthor
    Inspiring
    April 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.