Some problems with rouding in Lucee 5 and 6: ```&l...
# lucee
r
Some problems with rouding in Lucee 5 and 6:
Copy code
<cfscript>
writeOutput(
    decimalFormat(511.925) 
    & "<br>" & decimalFormat(512.925)
    & "<br>" & decimalFormat(654.925)
    & "<br>" & decimalFormat(655.925)
);
</cfscript>
output:
Copy code
511.93
512.92
654.92
655.93
dollarFormat
and
numberFormat
work better for these values
Copy code
<cfscript>
writeOutput(
    right(dollarFormat(512.925), -1)
    & "<br>"
    & numberFormat(512.925, "9.00")
);
</cfscript>
output:
Copy code
512.93
512.93
a
Looks like Lucee is using
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.
I'd say it's a bit fishy to use different rounding "algorithms" for different functions though..?
r
@Adam Cameron I believe it something worse... You can see in my example, that 511.925 is rounded to 511.93, but 512.925 to 512.92
a
Yes. That is
HALF_EVEN
, isn't it?
Comparing CF2021 and Lucee 5.4: https://gist.github.com/adamcameron/0bac4889614c7fbad1177f0b88f68861
Copy code
<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>
CF:
Lucee:
(That gist won't run on trycf.com due to
evaluate
)
At any rate... I think CF should not have changed approach, and Lucee should do the same thing as CF. And unless there's a good reason, both should be using the same approach across all those rounding examples. Doesn't seem like anyone's acquitting themselves particularly well with this one.
And now that I engage my brain, a more portable version of that code: https://trycf.com/gist/85db38fbf3ddb77bb129ac0588e76721/acf2023?theme=monokai
👍 1
r
a
I think you need to elaborate a bit on "wrong". Your example is rounding in a completely legit way:
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.
(added a version of the above to the ticket, and suggested it's clarified a bit).
r
@Adam Cameron I can't understand why rounding 511.925 to 511.93 and rounding 512.925 to 512.92 (and 513.925 to 513.92) are "absolutely nothing wrong". If Lucee use HALF_EVEN (rounding towards the even neighbor) it always must be .92? It can't use difference rounding for one function, but it looks like it does. I think that problem is not about rounding mode, but about decimal precision things... Maybe I didn't understand HALF_EVEN mode correctly 😞
Copy code
for(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.
a
Towards the even neighbour integer. Only integers are even or odd, after all; so only the integer part of the number is considered when assessing the "neighbour".
r
@Adam Cameron then: 511.925 > 511.93 because of 512 -- ok 512.925 > 512.92 because of 512 -- ok 513.925 > must be 513.93 because closer even is 514? but it's 513.92
⭐ 1
a
Hrm. Yep, I see what you mean. Also JS disagrees with that one:
Copy code
> 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!
👍 1