Search code examples
coldfusiontype-conversion

ColdFusion double to long conversion error


Can somebody explain to me why ColdFusion (tested on 2016,2018 and 2021) is doing a wrong double to long conversion? I know it can mess things up for fractional values, but in this example, it is clearly an integer value.
This is the code:

<cfoutput>
<cfset a = 69.35>
#getMetadata(a)# #a#</br>

<cfset b = a * 100>
#getMetadata(b)# #b#</br>

<cfset c = int(b)>
#getMetadata(c)# #c#</br>
</cfoutput>

And this is the output:

class coldfusion.runtime.CFDouble 69.35
class java.lang.Double 6935
class java.lang.Long 6934

It is "sort-of" fixable by doing this:

<cfset d = int(javacast("string", b))>
#getMetadata(d)# #d#</br>

Returning

class java.lang.Long 6935

But I'm not really satisfied by this "solution"... Thanks!

EDIT: Because ColdFusion is running on top of java, I guess this is responsible for it:

public static void main(String[] args)
{
  double a = 69.35;
  double b = a * 100;
  System.out.println(b);
  long c = (int)b;
  System.out.println(c);
  long d = Math.round(b);
  System.out.println(d);
}

Output:

6934.999999999999
6934
6935

And most likely, ColdFusion is using int() and not round() to convert the double value to long... This is one of the "nice" side effects of a typeless programming language, which internally makes a mess of it. Makes me think of javascript ;-)

EDIT 2:
As Cameron pointed out, there is a difference between #b# and #b.ToString()#. The former is returning 6935, while the latter is returning 6934.999999999999. This is confusing in my opinion, but I'll keep it in the back of my head for in case I run into another strange problem with double/long values :-)

And to make it even a bit more confusion: int(ToString(b)) is returning 6935 while int(b.ToString()) is returning 6934...

<cfset a = 69.35>
#getMetadata(a)# #a#</br>
<cfset b = a * 100>
#getMetadata(b)# #b#</br>
#b.toString()# #ToString(b)#</br>

Is returning:

class java.lang.String 69.35
class java.lang.Double 6935 
6934.999999999999 6935

So, don't assume that b.ToString() is the same as ToString(b) ...


Solution

  • As @SOS touches on in their comment (not sure why they did not make it an "answer"?), the issue is not the conversion. The issue is that ColdFusion is displaying 69.35 * 100 as equalling 6935, which it isn't. And even ColdFusion doesn't really think it is.

    As far as most computing languages are concerned, 69.35 * 100 is 6934.999999999999 (check on JS, Python, Ruby etc if you like), due to issues with the inherent inaccuracy of representing decimal fractional values in a system that stores stuff in binary. I've written about this before: Floating point arithmetic with decimals.

    Internally ColdFusion is storing the result as 6934.999999999999:

    <cfset f = 69.35 * 100>
    <cfoutput>#f.toString()#</cfoutput>
    

    This yields:

    6934.999999999999
    

    So when you use int to take the integer portion of 6934.999999999999, you get 6934. That part is actually doing the job correctly! ;-)