0

My question is very straightforward. The following code should return true for all questions. But question 4 and upwards return false, my question is why?

1.  <cfif #Evaluate("2 - 1.52")# eq "0.48">true<Cfelse>false</cfif><br>
2.  <cfif #Evaluate("3 - 2.52")# eq "0.48">true<Cfelse>false</cfif><br>
3.  <cfif #Evaluate("4 - 3.52")# eq "0.48">true<Cfelse>false</cfif><br>
4.  <cfif #Evaluate("5 - 4.52")# eq "0.48">true<Cfelse>false</cfif><br>
5.  <cfif #Evaluate("6 - 5.52")# eq "0.48">true<Cfelse>false</cfif><br>
6.  <cfif #javacast("double",Evaluate("6 - 5.52"))# eq "#javacast("double",0.48)#">true<Cfelse>false</cfif><br>
7.  <cfif compare(javacast("double",Evaluate("6 - 5.52")),javacast("double",0.48))>true<Cfelse>false</cfif><br>
James A Mohler
  • 11,060
  • 15
  • 46
  • 72
Nebu
  • 1,753
  • 1
  • 17
  • 33
  • 2
    OT: it seems strange that you are doing all these comparisons as string compares – James A Mohler Mar 24 '19 at 17:20
  • Possible duplicate of [Why does 0.06 + 0.01 = 0.07 in ColdFusion?](https://stackoverflow.com/questions/7422510/why-does-0-06-0-01-0-07-in-coldfusion) – SOS Mar 24 '19 at 17:24
  • This question comes up a lot. See It's not due to using evaluate() (though it doesn't look like that's needed here). CF uses an approximate type (double) and values aren't really equal to "0.48" (exactly). That's why the output is false. For example: `#Evaluate("5 - 4.52").toString()#` https://cffiddle.org/app/file?filepath=3a4a06e9-4037-482f-be86-bebd79a00a59/a67e0c9b-3218-4d2f-8281-00a617fa56d6/86c6c680-3a22-481f-a297-791fcf23a11a.cfm – SOS Mar 24 '19 at 17:31
  • 3
    I'll throw out my standard PSA again: If your application does anything with payments or any other information that shouldn't be open to the interwebs, then you should seriously take a look at upgrading to a more modern version of CF. CF11 is about to go EOL, and CF9 is extremely out of date and easily exploitable. Plus, the language changes since CF9 are _MAJOR_ improvements. – Shawn Mar 24 '19 at 18:58

1 Answers1

3

First I ran your code with

1.  <cfif #Evaluate("2 - 1.52")# eq "0.48">true<Cfelse>false</cfif><br>
2.  <cfif #Evaluate("3 - 2.52")# eq "0.48">true<Cfelse>false</cfif><br>
3.  <cfif #Evaluate("4 - 3.52")# eq "0.48">true<Cfelse>false</cfif><br>
4.  <cfif #Evaluate("5 - 4.52")# eq "0.48">true<Cfelse>false</cfif><br>
5.  <cfif #Evaluate("6 - 5.52")# eq "0.48">true<Cfelse>false</cfif><br>

Then I rand it without string compare

1.  <cfif Evaluate("2 - 1.52") eq 0.48>true<Cfelse>false</cfif><br>
2.  <cfif Evaluate("3 - 2.52") eq 0.48>true<Cfelse>false</cfif><br>
3.  <cfif Evaluate("4 - 3.52") eq 0.48>true<Cfelse>false</cfif><br>
4.  <cfif Evaluate("5 - 4.52") eq 0.48>true<Cfelse>false</cfif><br>
5.  <cfif Evaluate("6 - 5.52") eq 0.48>true<Cfelse>false</cfif><br>

I got

enter image description here

Next I just <cfoutput>ed the valued

<cfoutput>
1.  #Evaluate("2 - 1.52")#<br>
2.  #Evaluate("3 - 2.52")#<br>
3.  #Evaluate("4 - 3.52")#<br>
4.  #Evaluate("5 - 4.52")#<br>
5.  #Evaluate("6 - 5.52")#<br>
</cfoutput>

enter image description here

Which at first glance looks the same

But consider

<cfoutput>
1.  #(2 - 1.52 - 0.48)#<br>
2.  #(3 - 2.52 - 0.48)#<br>
3.  #(4 - 3.52 - 0.48)#<br>
4.  #(5 - 4.52 - 0.48)#<br>
5.  #(6 - 5.52 - 0.48)#<br>
</cfoutput>

enter image description here

You are basically hitting a rounding issue. ...E-016 is a lot of digits to the left.

How I deal with these If a ColdFusion variable is a number, just about anything can turn it into a floating point. You are using decimals, that is why it became a floating point. Floating points are (almost) always approximations. As such, you cannot do direct comparisons without side effects like this.

If you really had to do comparisons like this, multiply by 100 (an integer), and cast the values to integers and you should be in better shape.

Kind of OT

Some time is work with the Square's eCommerce API. All the money is measured in cents. It takes a while to get used to, but they do it to avoid problems like this.

Update based on Ageax suggestion

Consider #Evaluate("5 - 4.52").toString()#
#PrecisionEvaluate(5 - 4.52).toString()#

This has results of

enter image description here

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
  • If you're going to go to the trouble of multiplying by 100 then casting as an integer, why not just use PrecisionEvaluate() instead? – SOS Mar 24 '19 at 19:31
  • 1
    I just looked at https://stackoverflow.com/questions/3413448/double-vs-bigdecimal and precisionEvaluate() does seem interesting – James A Mohler Mar 24 '19 at 21:51
  • 2
    "Technically" floating points _are always_ approximations. Just sometimes the numbers they represent lie on a boundary that makes them pretty accurate approximations. :-) – Shawn Mar 24 '19 at 23:49