Where do I report bugs? I'm using CF2023 latest pa...
# adobe
j
Where do I report bugs? I'm using CF2023 latest patch level. This code is returning the wrong value...
ceiling((val/4)*100)
should be returning 7001 not 7002
Copy code
<cfset val = 280.04>

val/4: #(val/4)#
<br>
(val/4)*100: #((val/4)*100)#
<br>
ceiling((val/4)*100): #(ceiling((val/4)*100))#
<br>
ceiling((val/4)*100)*100:#(ceiling((val/4)*100)/100)#
m
it's known, and they likely won't treat it as a bug. floating point math is dumb. your val/4*100 has an extra number like 12 digits down, causing your ceiling to round it up. precisionEvaluate() the stuff before the ceiling should solve it.
j
Wow, crazy.
m
On the positive side, the next time you hit it, you'll be like, oh yeah... so stupid, and fix it quickly, or at least that's what happens to me every time
j
Yeah, I'll for sure remember it next time I have the issue. I'll probably just get in to the habit of using PrecisionEvaluate() when I want to use ceiling() / floor() for rounding up/down like this.
@Matt Jones thanks for the quick response btw.
We automatically promote large numbers and floating point math to use BigDecimal, which retains precision. Lucee also did this, but I'm unclear if they still do it in Lucee 7 as they changed their defaults.
1
using
precisionEvaluate()
in ACF should give the expected response.
d
I seem to recall Lucee backed off high precision (at least by default) because of the overhead it introduced. I believe it's now an option in the Admin if you want to enable it.
b
Yeah. BoxLang implemented a smarter parser where
Copy code
foo = 5
is still a
java.lang.Integer
behind the scenes. We only promote to a "larger" type such as double, long, or bigdecimal if it's needed, and our types are "contagious" just like other JVM languages such as Clojure, so the result of a math operation is the "larger" of the two types. This keeps most types pretty small. I don't know what Lucee's BigDecimal defaults were, but BoxLang also uses
DECIMAL128
by default > IEEE 754-2019 decimal128 format, 34 digits, and a rounding mode of HALF_EVEN. which stores a lot less data than what you get out of the box to be more performant. If Lucee used BigDecimal's default settings, it would have been unlimited precision which is entirely unnecessary for most use cases. I did tests on the ColdBox framework, and out of thousands of math operations on every page request (largely internal loops and counters), I think there was an average of 0 to 1 of them which actually used big decimal. The rest used basic int math. Lucee's approach of making EVERYTHING big decimal was killing ants with a hand-grenade IMO. And Lucee's fallback approach is still too heavy IMO as they use Doubles even for non-floating point numbers, or at least they did last time I looked.
And Adobe's approach is Leiningen Versus the Ants, lol
b
Hi @Jason Roozee, I don't think that the issue you are seeing has anything to do with Adobe ColdFusion. It results from the intricacies of floating-point arithmetic. Here's a quirk, just for the fun of it:
Let x be 0.99999999999999999... (ad infinitum)
Then 10x = 9.99999999999999...
= 9 + 0.999999999999999...
= 9 + x
If 10x = 9 + x, then x must be 1.
Therefore 0.9999999999999... = 1.
Different digits on the left and on the right of the equality sign, but the same value. I suppose the moral is: be careful how you mix floating-point arithmetic with integer arithmetic.
b
@BK BK Yes and no. Floating point math has the same pitfalls in pretty much every programming language. But Adobe CF also doesn't take steps to counter against it as BoxLang and Lucee do. In other words, some high level languages automatically promote to datatypes which can ensure accuracy in floating point operations. Adobe CF does not.
b
My point is, when dealing with floating-point arithmetic, some of the responsibility for accuracy rests with the developer. For example, suppose,
val = 280.04
. Then, in floating-point arithmetic, the value of
val/4
is not necessarily
70.01
. Instead, it is somewhere between
70.01 - e
and
70.01 + e
. Here e is a very small number that is a measure of the accuracy and granularity with which real numbers are stored and manipulated by the software or computer. Variously called "machine epsilon (macheps)", "floating-point precision" or just "precision", e is a deviation from the true value, and so may carry a positive or negative sign. It is the developer's responsibility to be aware of this, irrespective of the underlying the software or computer. For example, in floating-point arithmetic,
ceiling((val/4)*100)
may turn out to be
ceiling((70.01 - e)*100)
, which is
7001
or
ceiling((70.01 + e)*100)
, which is
7002
.
🤔 1
j
Well now that I know it's floating point issue, it makes total sense why it was happening. I didn't even think of that initially. I agree, the accuracy rests in the developer.. but also in those writing the documentation. Nothing in Adobe CF's documentation cautions about this potential issue and that the Java backend is using floating-point.
b
It's one of those things almost never mentioned in docs, but well known by everyone who's run into it at least once. And it's not even Java. Pretty much every language has the potential for this.
ACF could have worked harder to make this work out-of-the-box like BoxLang and Lucee do, but it is what it is
People often expect floating point math will only be an issue for numbers they feel are "large", but it's not about size per se, it's about representation. A simple example is that it's impossible to store the "simple" fraction 1/3 in decimal. You wind up with
0.333333333
repeating forever. This is because 1/3 cannot be stored exactly in decimal. So even though the value seems "small" there is no exact representation of it, so the value gets rounded at some point.
The same is true in binary, but for different numbers. For example the decimal value of
0.1
looks very "small", but cannot be stored exactly in binary without an infinite repeating pattern, which is eventually rounded.
Copy code
0011111110111001100110011001100110011001100110011001100110011010... etc
☝️ binary representation of the decimal
0.1
So when what appears to be a "simple" math operation is performed against a floating point value which has no exact binary representation is when this sort of behavior creeps in.
Java number classes such as BigDecimal store a large number of decimals and then optionally apply a smart rounding step to "correct" the result to what you likely expected.
You can reproduce this as simply as
Copy code
num = .1 + .2
writeoutput( num.toString() )
BTW. The output is
Copy code
0.30000000000000004 // Adobe 2025
0.3                 // BoxLang
b
👍