Incorrect multiplication result for large numbers

There is an issue with multiplying large numbers…

<cfset result = 4745484599433 * 9291>
#result# <!— result = 44090297413332000 → should be 44090297413332003 —>

an incredibly horrible workaround exists… but man oh man…

<cfset first = createObject(‘java’, ‘java.math.BigInteger’).init(“4745484599433”)>
<cfset second = createObject(‘java’, ‘java.math.BigInteger’).init(“9291”)>

#result#

Don’t forget to tell us about your stack!

OS: Windows 10
Java Version: java version “1.8.0_241”
Tomcat Version: Apache Tomcat/9.0.24
Lucee Version: Lucee 5.3.7.48

Have you compared the result with other CFML engines? (I get 4.40902974133 when using ACF2016-2021 on TryCF.com and BigInteger doesn’t provide better accuracy.)

Yes the other cf engines provide the scientific notation of the result

4.40902974133E+016.

Unfortunately a portion of my workaround was removed when submitting the ticket in order to prevent xss apparently. Ive put the example now in a pre-formatted text block to highlight it. This produces the correct result.

<cfset first = createObject('java', 'java.math.BigInteger').init("4745484599433")>
<cfset second = createObject('java', 'java.math.BigInteger').init("9291")>
<cfset result = first.multiply(second).toString()>
<cfoutput>#result#</cfoutput>

without the toString() immediately after the multiply method call the accuracy is lost and we end up with the same result as the simple multiplication. Yes you heard that right, a String is more accurate than the numeric value. Attempting to perform any further mathematical operations (without first converting it to a bigInt again) or numeric formatting on the string we lose the accuracy.

The Java equivalent (which works correctly) is:

Long result = Long.parseLong("4745484599433") * 9291;
//or
Long result = 4745484599433L * 9291;
System.out.println(result);

This is clearly a bug one that in all the engines as a result of the number not being a simple integer. The issue is unfortunately pervasive… Chrome developer tools:

4745484599433 * 9291 = 44090297413332000
// by indicating bigInt multiplication the precision is retained
4745484599433n * 9291n = 44090297413332003n

-However I am not concerned with the javascript implementation at this point, I am more concerned with the implicit casting and toString in Lucee

Cheers,
Gary

@gary_gilbert Use PrecisionEvaluate() PrecisionEvaluate() :: Lucee Documentation to get precise result.

2 Likes

@cfmitrah really? Wow… super… hey thanks… why didn’t I think of using that? oh wait, I did…

<cfoutput>#precisionEvaluate(4745484599433 * 9291)#</cfoutput>
// 44090297413332000 -> incorrect

pass in the math as a string :wink: otherwise you still are doing normal cfml math

2 Likes

well shit, the examples on cfdocs are incorrect. I read the description, where it said string but paid attention to the examples!

Thanks Zack that did it…

1 Like

Well knock me down with a feather!

CFSCRIPT-REPL: precisionEvaluate(4745484599433 * 9291)
44090297413332000
CFSCRIPT-REPL: precisionEvaluate("4745484599433 * 9291")
44090297413332003
4 Likes

We can and should fix the documentation and it’s examples to ensure it is clear that at the moment you have to use a string.

But nonetheless… is something that can be fixed, “properly”?
In that - surely, using strings around numbers for a maths operation is wrong? Isn’t it?
And I appreciate that CFML has no “real” types - it’s all implicit…
AND I am not saying Don’t allow strings to work either.

But at least make NUMBERS work too - for maths operations.

The docs in Lucees documentation, including the examples are correct. There is even a video there with more detailed information that micha thankfully published. Also, the docs of cfdocs.org states it expects a string as an argument. It’s just the examples that need to be corrected.

I’m no expert, so if anybody can correct me I’d appreciate that… but I’d say that any numeric expression passed as an argument will be evaluated at runtime even before being passed to the function. Thus it will pass an incorrect precalculated and unprecise value before the function gets anything about it. I’m totally sure the Lucee devs (and also ACF devs) would have done that immediately right away if possible. But because that simply wouldn’t work, they made the function expect a string as the argument.

I’d rather make a type checking for that argument variable and throw an error if the passed argument isn’t a string as expected.

By the way @Gavin_Baumanis I’m really appreciating all the PRs you are submitting to the docs and all the ideas you are adding to the community. Great help! Thank you for that!!!

1 Like

Thanks @andreas
And I will keep asking questions, too.
I genuinely have no problem with asking the “stupid” questions.
I have no issue with being wrong.
I am here to learn - even if that means learning I was wrong and being an idiot, at the time!
And it also comes with a “Never hesitate to apologise”, caveat, too!
Sorry I mislead you / Sorry I was wrong / Sorry I was rude / (and especially) Sorry I hurt you.
With genuine empathy and a sincere promise to, at worst, try to be better!

Getting back on track…
I have no idea if there IS a way to make it work for “numbers” (not strings) or not. But if I don;t ask aloud, then I’ll always be wondering. And the answer of course, gives us an opportunity to improve the documentation - as to why it is, and must be, like “this”.

Gavin.

2 Likes

@andreas,

Yes, you are right the Documention at Lucee are correct (a video - does anyone actually watch those?), but quite honestly cfdocs.org is my one-stop when looking stuff up… and yes as I mentioned in my comment above I skipped over the actual description and went right to the examples (TL;DR - my bad), so when mithra said to use precisionevaluate - with just a link to the docs, and I had already tried that and found it also “not working” well It triggered an RTFM… again My bad.

Main thing is that precisionevaluate unfortunately works as documented. It would be very cool if lucee cfml could implicitly detect a maths operation with a long/bigint and convert it accordingly.

cheers

@andreas is correct, unless you use a string, the numbers are already converted before it reaches precision evaluate.

Lucee 6.0 introduces a this.preciseMath = true option for Application.cfc which improves the internal accuracy / handling of such numbers

https://luceeserver.atlassian.net/browse/LDEV-3729
https://luceeserver.atlassian.net/browse/LDEV-3662

3 Likes

@gary_gilbert

cfdocs is still on my short list.
Since docs.lucee.org and javadic.lucee.org have Lucee specific info that makes it important (for me) to use. Especially after I learned about the linked Lucee test cases as seeing a few examples helps (me) digest the text better.

I agree,
It can say “use a string” and since I’m doing math, thinking about numbers I’m reading “pass your math equation”.

I wanted to test precisionEvaluate on TryCF.com and CFFiddle. TryCF displays a “Sorry, some CF functions/tags are disabled.” message and CFFiddle throws a CF error.

FYI: I tested it on unsupported ColdFusion 2016 server and didn’t have to use quotation marks to prevent “normal cfml math” from occurring. Both approaches returned the same result. (ie, NUMBERS work.) This information is good when writing cross-compatible CFML applications.

1 Like