romanstr
12/28/2023, 12:34 PM<cfscript>
writeOutput(
decimalFormat(511.925)
& "<br>" & decimalFormat(512.925)
& "<br>" & decimalFormat(654.925)
& "<br>" & decimalFormat(655.925)
);
</cfscript>
output:
511.93
512.92
654.92
655.93romanstr
12/28/2023, 12:42 PMdollarFormat and numberFormat work better for these values
<cfscript>
writeOutput(
right(dollarFormat(512.925), -1)
& "<br>"
& numberFormat(512.925, "9.00")
);
</cfscript>
output:
512.93
512.93Adam Cameron
HALF_EVEN rounding (https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html#HALF_EVEN). CF used this until CF11, but then changed to HALF_UP (or maybe just UP... I did not check). I'd say this is a bug in CF2016+, because there was no good reason to change this.
@Mark Takata (Adobe) can you get to the bottom of why this changed? I can find nothing public which identifies it as a conscious decision to make this breaking change.Adam Cameron
romanstr
12/28/2023, 2:31 PMAdam Cameron
HALF_EVEN , isn't it?Adam Cameron
<cfscript>
numbers = [511.925,512.925,654.925,655.925]
functions = [
"decimalFormat(##x##)",
"right(dollarFormat(##x##), -1)",
'numberFormat(##x##, "9.00")'
]
</cfscript>
<cfoutput>
<table border="1">
<tr>
<th>f\x</th>
<cfloop index="x" array="#numbers#"><th>#x#</th></cfloop>
</tr>
<cfloop index="f" array="#functions#">
<tr>
<td>#f#</td>
<cfloop index="x" array="#numbers#">
<td>#evaluate(f)#</td>
</cfloop>
</tr>
</cfloop>
</table>
</cfoutput>Adam Cameron
Adam Cameron
Adam Cameron
evaluate)Adam Cameron
Adam Cameron
romanstr
12/29/2023, 10:25 AMAdam Cameron
HALF_EVEN is a well-defined rounding strategy. To be clear, taken in isolation, there is absolutely nothing wrong that I can see with your repro case.
The issue is that it doesn't match CF after CF changed its behaviour between CF11 and CF2016. That it doesn't match CF here is debatable as to whether it's even a compat bug until Adobe can explain why it changed. if it's by design, and documented somewhere public: it's legit, and Lucee should have also changed. if it's a fuck up (let's face it... it probably is), then the bug is with CF not Lucee.
The other potential issue is that Lucee is not internally consistent with its rounding behaviour across various rounding functions. There might be reasons for this. if there are: it's a documentation issue. If there's not: it's a bug. Either way they have something to fix there.
But what you've raised on your ticket is... "incomplete" at best.Adam Cameron
romanstr
12/29/2023, 9:54 PMromanstr
12/29/2023, 9:56 PMfor(i=411.925; i < 900; i++) {
writeOutput(decimalFormat(i) & "<br>");
}
See this example. The values are rounded to .93 before 511.925, after that they are rounded to .92 till 654.92, after that .93 again.Adam Cameron
romanstr
12/29/2023, 10:03 PMAdam Cameron
> 513.925.toFixed(2)
<- '513.92'
When I say "disagrees", I mean "with Lucee". It's doing it right.
CF is correct >= CF2016; does the same as Lucee prior to that.
I wonder if Lucee and <= CF11 are using another one of these schemes (https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html)
TBH a) my interest in assessing this situation is waning, but I'll keep an eye on the ticket and sanity check (for my definition of "sanity", which seems a bit wayward here ;-)); b) it's Fri evening and I've had beer, and I'm not gonna dick around with how numbers round any further for now.
If you had the motivation, writing a test rig that puts your numbers through Java's rounding, using each RoundingMode might be useful: see if you can find a match for Lucee / old-CF's behaviour. Given they both match <=CF11, I am thinking there's a reason for how it was/is, and we're missing something.
Good work for sticking at it, and calling me out!