Using ColdFusion's PrecisionEvaluate() Function To Perform Big Integer Math
Did you know that ColdFusion has a function named PrecisionEvaluate()? Neither did I. I randomly came across it the other day when I was looking up information about ColdFusion's integer division operator. PrecisionEvaluate() works exactly like the Evaluate() function except for that it uses BigInteger objects when performing mathematical operations.
Before we look at how the precisionEvaluate() function works, let's take a look at the normal behavior of big integer math in ColdFusion:
 <!
 Create two numbers that are too large to be signed integers in
 ColdFusion. These are strings now, so shouldn't pose a problem
 until we try to do math with them.
 >
 <cfset hugeNumberA = repeatString( "1", 32 ) />
 <cfset hugeNumberB = repeatString( "2", 32 ) />

 <! Perform huge integer math! Mwaa ha ha ha ha! >
 <cfoutput>

 Number A: #hugeNumberA#<br />
 Number B: #hugeNumberB#<br />

 Sum: #(hugeNumberA + hugeNumberB)#<br />
 Product: #(hugeNumberA * hugeNumberB)#

 </cfoutput>
As you can see, I'm using signed integers larger than 31 bits (the largest integer that ColdFusion can properly model with precision). When we run the above code, forcing the huge number "strings" to become "numbers", we get the following output:
Number A: 11111111111111111111111111111111
Number B: 22222222222222222222222222222222
Sum: 3.33333333333E+031
Product: 2.46913580247E+062
As you can see, the mathematical outcomes of the large number operations can only be held to a loose precision.
Now, let's do the same exact thing; however, this time, we are going to perform the mathematical operations in the context of the precisionEvaluate() function:
 <!
 Create two numbers that are too large to be signed integers in
 ColdFusion. These are strings now, so shouldn't pose a problem
 until we try to do math with them.
 >
 <cfset hugeNumberA = repeatString( "1", 32 ) />
 <cfset hugeNumberB = repeatString( "2", 32 ) />

 <!
 This time, we are going to perform math on the two numbers using
 precisionEvaluate() which uses BigInteger objects to perform the
 mathetmatical operations.
 >
 <cfoutput>

 Number A: #hugeNumberA#<br />
 Number B: #hugeNumberB#<br />

 Sum: #precisionEvaluate( hugeNumberA + hugeNumberB )#<br />
 Product: #precisionEvaluate( hugeNumberA * hugeNumberB )#

 </cfoutput>
As you can see, all we've done is wrapped our mathematical operations in a call to the precisionEvaluate() function. When we run this code, we get the following output:
Number A: 11111111111111111111111111111111
Number B: 22222222222222222222222222222222
Sum: 33333333333333333333333333333333
Product: 246913580246913580.......5308641975308641975308642
NOTE: I have removed a few digits to prevent wrapping on the blog.
This time, both the sum and product operations were able to preserve full precision.
One thing you might notice in the above code is that the statement we passed to the precisionEvaluate() function is not quoted. The documentation on this function states that if you're just performing arithmetic expressions, using nonquoted values will result in faster execution. I have to assume that this is a compiletime optimization since one would think that performing the math first would negate the use of precisionEvaluate().
After the precisionEvaluate() function executes, it returns a big decimal value that represents the result of the last expression evaluated (remember, the evaluate() functions can execute multiple statements). This result can then be used to perform further large number arithmetic; or, it can be used in standard math and will be converted back to a normal numeric value automatically.
To get a sense of what actually comes back from precisionEvaluate(), let's loop over the methods that are available on the resultant object:
 <! Get the big integer result. >
 <cfset bigResult = precisionEvaluate( 3 * 3 ) />

 <! Get the methods available on this result. >
 <cfset methods = bigResult.getClass().getMethods() />

 <cfoutput>

 <! Output the result's class type. >
 Class: #bigResult.getClass().getName()#<br />
 <br />

 <! Loop over each method to output it's name. >
 <cfloop
 index="method"
 array="#methods#">

 #method.getName()#<br />

 </cfloop>

 </cfoutput>
This time, we are executing really simple math; however, our precisionEvaluate() method will use BigInteger values regardless. After performing the big integer math, we are outputting the resultant class type and its methods (NOTE: I have removed some duplicate method names):
Class: java.math.BigDecimal
add
equals
toString
hashCode
abs
pow
min
max
compareTo
valueOf
intValue
longValue
floatValue
doubleValue
signum
round
ulp
scale
subtract
multiply
divide
divideAndRemainder
remainder
negate
toBigInteger
scaleByPowerOfTen
unscaledValue
precision
divideToIntegralValue
plus
setScale
movePointLeft
movePointRight
stripTrailingZeros
toEngineeringString
toPlainString
toBigIntegerExact
longValueExact
intValueExact
shortValueExact
byteValueExact
byteValue
shortValue
As you can see, the result is a BigDecimal instance and provides all of the native BigDecimal methods.
Of course, you don't have to use the result as if it were a special class; you can simply use the result in further math and ColdFusion will convert it back to a native numeric value:
 #(precisionEvaluate( 3 * 3 ) + 1)#
... which gives us the expected:
10
The BigInteger class has a lot of useful functionality; but, it's nice to know that ColdFusion provides a way to perform large number mathematical operations without having to dip into the Java layer. Now that I know this function is here, I am sure I will be able to find a good use for it.
Reader Comments
Wow. This looks to be the EASIEST way to do BigIntegers. Thanks!
What an interesting find. ColdFusion was difficult for me when I was first starting to learn it, and anything math makes me uncomfortable, so I read this post expecting to be made exceptionally uncomfortable (but I've learned, the only way you learn is to let yourself sit in those uncomfortable feelings). Instead, I found it super interesting and oddly comfortable. Will I ever do anything that needs it? Maybe not, but I know I could if I needed to, now. Who knew CF (and especially math) could do that :)
@WebManWalking,
Yeah, this is super easy. I could also see myself very easily becoming lazy about creating BigInteger instances :) For example, it's way easier to do this:
... than it is to do this:
Of course the result of precisionEvaluate() is a BigDecimal, not a BigInteger; but still, you know what I'm saying.
@JJ,
I am super flattered that I was able to write about this in a way that allowed you to keep your cool :) That's exactly the kind of feeling that I want to create with my explorations.
did i know about PrecisionEvaluate()? why yes i did ;)
http://bit.ly/9Pjgjm
@Paul,
Ha ha, awesome :)
I actually didn't know about PrecisionEvaluate() till today! Greg Moser introduced it to me. We were looking at some simple math that rocked our world!
Try this for fun:
cfdump var="#89.95 + 13.29 eq 103.24#" ==> false
cfdump var="#PrecisionEvaluate(89.95 + 13.29) eq 103.24#" ==> true
This is math issue. Supposedly, there is only finite set of floating point values that can be represented by computers.
So, you can really never accurately do equality comparisons on FP values.
Good Read:
http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
I am a big fan, Ben. Your blog is my go to source when I need to deconstruct a serious challenge. Thank you, thank you! And, your posters are just as informed and informative. You've got a great thing going here.
I came across this post while researching PrecisionEvaluate(). Here's another shocker:
In cf9, at least, you'll get "1640,1639". I've just had a bug request labeled as "NotABug" and withdrawn because this is the fault of a floating point calculation. It was suggested that "If I require precision", I should use PrecisionEvaluate() around the inner value in int().
Anyone else run across this, because that's pretty unintuitive, and certainly not commonly recommended practice..? Makes me wonder how many inaccurate values I'm creating by using basic arithmetic functions. I'm now doubting each of the thousands of uses of int() and round(),etc. I've written into our code over the years. Details on this feature are here:
https://bugbase.adobe.com/index.cfm?event=bug&id=3402581
Who doesn't need precision?
Forewarned is forearmed, eh? ;)
Post A Comment