Strange Round behavious

I’ve got a weird rounding issue with Lucee 5.
To reproduce, paste this Code on trycf.com:

<cfscript>
	price=4.1; discount=25;

	result1 = price - (price*discount/100);				// 4.1 - 25% => 3.075
	writeOutput('result1 => ' & result1 & '<hr />');	// outputs 3.075
	resultRounded = Round(result1,2);					// this shoud round to 3.08
	//resultRounded = NumberFormat(result1,'99999.99');	// workaound, but not nice!?
	writeOutput('resultRounded (calculted) => ' & resultRounded & '<hr />');	// outputs 3.07 => wrong!

	result2=3.075;
	resultRounded2 = Round(result2,2);					// this should round to 3.08
	writeOutput('resultRounded2 (fix) => ' & resultRounded2 & '<hr />');		// outputs 3.08 => correct!

    dump(var=result1,label='result1');
	dump(var=result2,label='result2');
	if (result1 == result2) writeOutput('results are the same'); else writeoutput(result1 & ' and ' & result2 & ' are not equal!');
</cfscript>

Explanation: a price is calculated; it should be 25% of 4.1 which will be 3.075.
The first output just shows the calculated price; 3.075 => correct.
Now, the result should be rounded mathematically to two decimals; result should be 3.08.
BUT => resultRounded outputs 3.07 => wrong!

If I directly set result=3.075 and then do the rounding to that result; output is 3.08 => correct!

Also take a look at the last 3 lines => a dump of both - result1 and result2 - shows both are numbers and both with 3.075. But a comparison of those outputs that they are NOT equal!

Is this a bug?

In the code you see a workaround; if I would use “NumberFormat” of result1 than it will output 3.08. But that’s not “nice”; and also resultRounded would be a string not a number (of course, I could change it to a number with Val(), but that’s definitely not what I want…)

This piece is probably doing 1.02499999… in Java. So it is a bug.
Workaround is round(price*discount/100,2)

Note: If you want compatibility with Adobe, you need to use numberFormat.

I’ve been using this UDF to round since 2008. (It was on a 2003 Macromedia blog entry by Christian Cantrell.) I just tested it and appears to work on Lucee 5 (at least on TryCF.com.)

function decimalRound(numberToRound){
	var numberOfPlaces = 2;
	var mode = "even";
	var bd = createObject("java", "java.math.BigDecimal");
	bd.init(arguments.numberToRound);
	if (structCount(arguments) GTE 2 AND isNumeric(Arguments[2])){
		numberOfPlaces = val(Arguments[2]);
	}
	if (structCount(arguments) GTE 3 AND Listfindnocase("up,down", Arguments[3])){
		mode = Arguments[3];
	}
	if (mode is "up"){
		bd = bd.setScale(numberOfPlaces, bd.ROUND_HALF_UP);
	} else if (mode is "down"){
		bd = bd.setScale(numberOfPlaces, bd.ROUND_HALF_DOWN);
	} else {
		bd = bd.setScale(numberOfPlaces, bd.ROUND_HALF_EVEN);
	}
	return bd.toString();
}

I looked at and tested the round() code and it isn’t that. I have a feeling it is just the Java code behind Lucee during the arithmetic. I haven’t tested if tag version makes a difference versus all cfscript.

Just ran on trycf as tag and it does the same thing. Specifically it is the “-” minus operation. I separated the multiplication and division part to it’s own variable and it still comes out wrong, even if using val() or numberFormat(). If I use numberFormat() or val() or the minus part it comes out correctly.

<cfset pdiscount = price*discount/100>
<cfset result1 = val(price-pdiscount)>

I filed a bug for this issue.
https://luceeserver.atlassian.net/browse/LDEV-2778

1 Like

Added to bug some findings.

This happens on all engines in trycf. result1 DOES NOT equal result2 on any engine. ACF or Lucee/Railo.
The problem appears because Railo/Lucee has the precision variable for round() unlike ACF.
You can use val() or numberformat() for arithmetic and then result1 WILL equal result2.
round() with the precision is NOT portable to ACF.