Is this a thing? I', in the process of upgrading f...
# cfml-general
m
Is this a thing? I', in the process of upgrading from ACF2016 to 2021 and during testing I found this.. I have 3 mysql DB fields, total, tax, shiptotal. All defined as DECIMAL(12,2) When I query the row, and then, in CF, add them together
grandTotal=val(h.total)+val(h.tax)+val(h.shiptotal);
when serializing the total to send back in Ajax I get "847.8100000000001" but when I output it to page or cfdump the result I get 847.81, as expected.. edited to add, in ACF2016
b
You need to use precisionEvaluate(). You've run afoul of floating point math.
m
lol, yeah, but when you add 2 decimal places to 2 decimal places... where in the heck do the floaty bits even come from? I get it for * & /, but + and -? and why only during serialization? When did precisionEvaluate() get added, thanks for that pointer btw
b
Because decimal values that look simple for us don't have a simple way to represent them internally as binary
If you Google, there's info around the Internet. Lots of langs have this issue
s
i heard some great advice recently. work in cents. if we know its always 2 decimal places, multiply by 100 and do everything in whole numbers
3
m
damn, I hate it when someone pulls the binary sorcery card.
🙂 2
b

https://youtu.be/g7A8OFi1mdU

m
That's a good idea Scott - from today forward..sigh
s
same
m
at least in this case
replace(decimalformat(number,',','','all')
will solve it, I don't even want to think how many pennies I have lost (or gained) have happened before now.. reminds me of Richard Pryor's bank hack
😀 1
b
And this Joel On Software article actually has a better low-level explanation of why base 2 can't represent some "simple" decimals correctly https://www.joelonsoftware.com/2007/09/26/explaining-the-excel-bug/
1
Here's an excerpt from it
Copy code
Since 77.1 has no exact representation, Excel stores it as

0100 0000 0101 0011 0100 0110 0110 0110
0110 0110 0110 0110 0110 0110 0110 0110

and then when you try to multiply it by 850, you get something very close to 65,535, but not exactly 65,535, because of the fact that 77.1 wasn't stored exactly because that would take infinite memory.
Basically,
.1
in decimal looks very simple, but has no direct way to represent it in base 2 without an infinitely repeating pattern. Sort of by the same tokek that the "simple" fraction of
1/3
can't be accurately represented in decimal because it's .3333 forever and ever.
m
Sad thing is, right after you said binary, I got it, but in all my years of doing this it never occurred to me that adding and subtracting the same decimal places would cause it to rear its ugly head. * & / I have always kept an eye out for it. New trick for an old dog.. I'll be formatting all decimals from all calculations from now on; thankfully I don't work on anything that will change the world as we know it..
b
Yeah, I get it. It's one of those things that smacks you up side the face one day and you can't believe the bedrock of basic math operations has the ability to go sideways so easily.
Lucee 6 is entertaining a feature to use BigDecimals instead of Doubles everywhere to effectively make this always "just work" at the cost of some memory overhead.
m
I switched from dbtype float to decimal for something similar I noticed years ago with *&/, I kinda feel stoopid that I didn't even consider +&-
Programming - where math isn't really math, just kinda math-ey
😜 2
b
Here's a fun trick to visualize the binary representation of a given decimal. I ran this from the CommandBox REPL. The first one is
.1
and the second one is
.5
Copy code
CFSCRIPT-REPL: createObject( 'java', 'java.lang.Long' ).toBinaryString( createObject( 'java', 'java.lang.Double' ).doubleToLongBits( .1 ) )
11111110111001100110011001100110011001100110011001100110011010

CFSCRIPT-REPL: createObject( 'java', 'java.lang.Long' ).toBinaryString( createObject( 'java', 'java.lang.Double' ).doubleToLongBits( .5 ) )
11111111100000000000000000000000000000000000000000000000000000
You can see how .5 has a nice exact representation in binary, but .1 repeats for ever, bringing about certain doom (and rounding)
m
now you are just getting a little too "weedy" for me, I'll keep to the trees
🌳 1
b
Avoids any jokes about "weed" needed to understand....
m
puff puff pass..
found this cool infographic on the topic