Thursday, April 10, 2014

ColdFusion 10 CFLOOP bug with decimal steps

Back in the ColdFusion MX 6.1 era, I stumbled across a bug. A CFLOOP tag with a step of 0.2 would inexplicably skip a number (32.0, I believe) as it counted up. The bug persisted through version 7.x. I had a workaround in place, so I didn’t give it much thought after that.

Recent ColdFusion conversations made me think of this bug, so I decided to code up a quick test to see if it was still an issue with CF10. What I discovered was far worse than I remembered.

Start with a simple loop from 0 to 40 with a step of 0.2. Display each step and add a comma after all but the last number:
<cfloop index="i" from="0" to="40" step="0.2">
      <cfoutput>#i#</cfoutput>
      <cfif i LT 40>, </cfif>
</cfloop>

Hmmm… 40 is missing. When counting up by integers, 40 is included. This was the first hint that something was not quite right.


The list is a bit tough to read, so let’s break it up by inserting a <BR> before each integer (skipping the initial zero).

<cfloop index="i" from="0" to="40" step="0.2">
      <cfif int(i) EQ i and i GT 0><BR></cfif>
      <cfoutput>#i#</cfoutput>
      <cfif i LT 40>, </cfif>
</cfloop>

This should have produced a nice column of integers with each row ending in x.8! 

There is a line break after 0.8, as expected, but no line breaks again until 8.8 and no more again until 37.8? This makes no sense. Since the problem occurs in the first 10 digits, let’s reduce the TO value to 10 and include “int(i)” and the result of the evaluation “int(i) eq i” in the output:
<cfloop index="i" from="0" to="10" step="0.2">
      <cfif int(i) EQ i and i GT 0><BR></cfif>
      <cfoutput>#int(i)# EQ #i#:#int(i) EQ i#</cfoutput>
      <cfif i LT 10>, </cfif>
</cfloop>


So, this output is telling me that when i = 2 , int(i) EQ i evaluatees to 1 EQ 2! Worse, it is saying 4 <> 4, 5 <> 5, 6 <> 6 but 7 = 7? I tried the other rounding functions: FIX, ROUND and CEILING with varying degrees of weirdness. (ROUND appeared to work, but I don’t trust it completely.) I also tried JavaCast() but the results were still off. The string comparison function COMPARE (x,y) had the same problem. When I tried int(2.0) by itself, it did not yield “1”, so I suspect the problem is data type conversion in the processing of the loop steps.

The Workaround
I used the same workaround here that I used years ago with the original problem. Multiply the FROM, TO and STEP by 10 and divide the value as appropriate inside the loop.
<cfloop index="i" from="0" to="100" step="2">
      <cfif i MOD 10 EQ 0 and i GT 0><BR></cfif>
      <cfoutput>#numberformat(i/10,"09.9")#</cfoutput>
      <cfif i LT 100>, </cfif>
</cfloop>

And the results turn out as expected:

Conclusion
Avoid decimal steps.

No comments:

Post a Comment