Tuesday 25 September 2012

I RTFM again, and learn something about evaluate() I didn't really know

G'day
Here's a quick one: I have 17min before I need to start work.  Tick tock.

The title of this is slightly misleading... I wanted to mention "evaluate()" because it's a controversial function, so the title would garner interest.  Whilst this involves evaluate() tangentially, it's more about a slightly-less-derided function precisionEvaluate().

15min.


In yesterday's article I needed to accurately process some big numbers, and the code I posted ended up using Java BigIntegers.  But initially it was using precisionEvaluate() to ensure I did not lose accuracy in some of my calculations.

Background: ColdFusion uses long integers or double-precision floating point numbers (depending on the operation) to do most of its mathematical calculations.  Long integers are pretty big, but not that big, and floating point numbers lose accuracy very quickly, just because the way they work.  Neither were any good for what I was doing, as I needed to do maths with 128-bit numbers.

11min.

So I started using precisionEvaluate() so that my calculations would maintain more accuracy given it uses BigDecimals.  That was all well and good until I realised I needed to do a MOD operation, which doesn't work on decimals, so I needed to go back to some flavour of integer, and I decided to use BigIntegers instead.

9min.

In reading the docs for precisionEvaluate(), I was reminded something I kinda knew already, but had forgotten: both evaluate() and precisionEvaluate() can process more than one expression, and a preceding expression's result can be used in a subsequent expression.  This is quite cool.

Here's an example in action:

resultBaseline = ((10000000000000000 * 2) + 1) - (10000000000000000 * 2);
resultImprecise = evaluate("result = 10000000000000000 * 2", "result = result + 1", "result - (10000000000000000 * 2)");
resultPrecise = precisionEvaluate("result = 10000000000000000 * 2", "result = result + 1", "result - (10000000000000000 * 2)");
writeDump(variables);

And the result:

struct
RESULT20000000000000001
RESULTBASELINE0
RESULTIMPRECISE0
RESULTPRECISE1

6min.

Notice in the code I am quoting the expressions, and each one is a full variable assignment, setting result as I go.  And each subsequeent expression uses the result of the previous one.

I think that's quite cool.  I'd like to be able to use some better approach than the variable assignment - eg: if each expression set some internal variable one could use automatically, rather than having to have the assignment.  But it's still quite handy.

And in this example I also show the reason precisionEvaluate() exists: it handles the accuracy properly, and we don't lose the "1" from the end of the number.  It's a contrived example, but you get my point.

4min.

Because I was adding individual digits to a number that was going to end up being 128-bits long, I needed to maintain this accuracy throughout my calculations.

So... hands up... who already knew you could do this with evaluate() and precisionEvaluate()?

3min... enough time for a quick proofread, and pressing "submit".

--
Adam