ColdFusion double to long conversion error

Viewed 113

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) ...

2 Answers

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! ;-)

I know I'm kind of late to the game with this answer, but here's what I've used in the past when encountering precision issues in ColdFusion when dealing with mathematical calculations using currency. In order to avoid precision errors, I've always wrapped my mathematical calculations with the precisionEvaluate() function. Using it with your sample code

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

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

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

The resultant output looks like this. As you can see, it converts it from a Double to a BigDecimal and it avoids the precision issues.

class coldfusion.runtime.CFDouble 69.35
class java.math.BigDecimal 6935.00
class java.lang.Long 6935

You can view the results here.

Related