Adobe ColdFusion uses java.lang.Double to represent numbers in most cases, and it's a floating point representation. That means it stores a finite amount of precision about the number in question, and discards any bits beyond that. The stored precision is relative to the magnitude of the number. So if you ask it to store a HUGE integer, you'll start losing the right-most (ones, tens, hundreds, etc. columns). For example:
#11111111111111111 EQ 11111111111111111# #11111111111111111 EQ 11111111111111113# #111111111111111111 EQ 111111111111111113# #44444444444444444 EQ 44444444444444444# #44444444444444444 EQ 44444444444444443# #444444444444444444 EQ 444444444444444443#
You'd probably expect to get "YES NO NO YES NO NO" output to the page, but you'd be wrong. What you'll actually get is "YES YES NO YES NO YES".
What's the solution for comparing large integers like this? To compare them as strings:
#compare(11111111111111111, 11111111111111111) EQ 0# #compare(11111111111111111, 11111111111111113) EQ 0# #compare(111111111111111111, 111111111111111113) EQ 0# #compare(44444444444444444, 44444444444444444) EQ 0# #compare(44444444444444444, 44444444444444443) EQ 0# #compare(444444444444444444, 444444444444444443) EQ 0#
Running this code will give you "YES NO NO YES NO NO" – the desired result.
I believe Railo also uses java.lang.Double to represent numerics, so I'd expect it to have the same issue, though I didn't check.
I remember the first time I ran into number problems – trying to NumberFormat() the dollar amount for a merger in the billions of dollars. CF didn't care to treat that as a valid number :) Good to know there are easy work arounds to some of the number limitations. Thanks.
Yeah, I discovered a bug in the round() function a while back, which seems to be related. I blogged about it back in February (http://bit.ly/3FyZnM) and reported it to Adobe's "wish/bug list" back then. I just confirmed that the bug still exists in ColdFusion 9 beta 1 as well as OpenBD 1.1. Railo does not suffer this bug (neither 3.0 nor 3.1).
Basically, #round(4000.5)# outputs 4001, and you'd expect #round(4.0005*1000)# to output the same, but AdobeCF and OpenBD output 4000 for the latter.
FWIW, for your first example above, Railo 3.1.0.22 outputs neither what you'd expect nor what Adobe CF outputs, but rather "true true true true false true." This suggests that I might be able to find a similar round() function "bug" in Railo as well.
I guess all that is to say: you make a good point, Barney :) Beware of floating point representations in CFML!
BTW, if you read my post at http://bit.ly/3FyZnM, you'll see that I solved my rounding issue in the same way: cast to string to "lose" the floating point representation. While I was at it, I had my round UDF also enable you to round to a specified decimal point.
This is not a problem with just CF but floating point in general. Floating point is an approximation at best of the real numbers. There are very few numbers in the Real Line that can be exactly represented by Floating point, the rest get rounded at some point, consider pi, 1/3, or sqrt(2). In the examples you present you have past ability for the Significand of the floating point system to represent your numbers. One of the strange oddities of Floating point is that its accuracy goes down as the number moves away from 0 on the real line. As a matter of a fact when you hit the number 1 you have already passed 50% of the numbers that Floating point can represent.
Due to these factors most bank software avoids floating point, and if you want big numbers you need to get yourself a large number package.
Ograll,
Even just explicitly using java.math.BigDecimal (and BigInteger) is perfectly sufficient in most cases. ColdFusion will treat it like any other java.lang.Number so you can use standard math operators on it and pass "numeric" type checks. Just a matter of ensuring you're using an instance of it and it's methods when it matters, which can be difficult, since ColdFusion uses java.lang.Double for everything. As such, you can START with floating point error if you're not careful.
In my example, using the string constructor to either "Big" class and then comparing would have been equally successful, but since simply comparing the strings I'd be passing to the constructors also worked, it saves a pile of typing. The moreso because I was actually comparing strings that just happened to be valid numbers (). Yay for implicit type conversions!