ColdFusion double to long 转换错误

ColdFusion double to long conversion error

有人可以向我解释为什么 ColdFusion(在 2016、2018 和 2021 年测试过)做错了 double 到 long 的转换吗?我知道分数值可能会把事情搞砸,但在这个例子中,它显然是一个整数值。
这是代码:

<cfoutput>
<cfset a = 69.35>
#getMetadata(a)# #a#</br>

<cfset b = a * 100>
#getMetadata(b)# #b#</br>

<cfset c = int(b)>
#getMetadata(c)# #c#</br>
</cfoutput>

这是输出:

class coldfusion.runtime.CFDouble 69.35
class java.lang.Double 6935
class java.lang.Long 6934

通过这样做“有点”可以修复:

<cfset d = int(javacast("string", b))>
#getMetadata(d)# #d#</br>

回归

class java.lang.Long 6935

但是我对这个“解决方案”不是很满意... 谢谢!

编辑: 因为 ColdFusion 运行ning 在 java 之上,我想这是造成它的原因:

public static void main(String[] args)
{
  double a = 69.35;
  double b = a * 100;
  System.out.println(b);
  long c = (int)b;
  System.out.println(c);
  long d = Math.round(b);
  System.out.println(d);
}

输出:

6934.999999999999
6934
6935

最有可能的是,ColdFusion 使用 int() 而不是 round() 将 double 值转换为 long...这是无类型编程语言的“好”副作用之一,它在内部使乱七八糟的。让我想起 java脚本 ;-)

编辑 2:
正如 Cameron 指出的那样,#b# 和 #b.ToString()# 之间存在差异。前者返回 6935,而后者返回 6934.999999999999。在我看来,这令人困惑,但我会把它放在脑后,以防万一我 运行 陷入另一个具有 double/long 值的奇怪问题:-)

并且让它变得更加混乱: int(ToString(b)) 返回 6935 而 int(b.ToString()) 返回 6934...

<cfset a = 69.35>
#getMetadata(a)# #a#</br>
<cfset b = a * 100>
#getMetadata(b)# #b#</br>
#b.toString()# #ToString(b)#</br>

正在返回:

class java.lang.String 69.35
class java.lang.Double 6935 
6934.999999999999 6935

所以,不要假设 b.ToString() 与 ToString(b) 相同 ...

正如@SOS 在他们的评论中提到的(不确定他们为什么不将其作为“答案”?),问题不在于转换。问题是 ColdFusion 将 69.35 * 100 显示为等于 6935,但事实并非如此。甚至 ColdFusion 也不这么认为。

就大多数计算语言而言,69.35 * 1006934.999999999999(如果您愿意,请检查 JS、Python、Ruby 等),由于问题在以二进制存储东西的系统中表示十进制小数值的固有不准确性。我之前写过:Floating point arithmetic with decimals.

ColdFusion 内部将结果存储为 6934.999999999999:

<cfset f = 69.35 * 100>
<cfoutput>#f.toString()#</cfoutput>

这产生:

6934.999999999999

因此,当您使用 int6934.999999999999 的整数部分时,您会得到 6934那部分实际上是在正确地完成工作! ;-)

我知道我对这个答案有点晚了,但这是我过去在使用货币处理数学计算时遇到 ColdFusion 中的精度问题时使用的方法。为了避免精度错误,我总是用 precisionEvaluate() 函数包装我的数学计算。将它与您的示例代码一起使用

<cfoutput>
<cfset a = 69.35>
#getMetadata(a)# #a#</br>

<cfset b = precisionEvaluate(a * 100)>
#getMetadata(b)# #b#</br>

<cfset c = int(b)>
#getMetadata(c)# #c#</br>
</cfoutput>

结果输出如下所示。如您所见,它将它从 Double 转换为 BigDecimal 并避免了精度问题。

class coldfusion.runtime.CFDouble 69.35
class java.math.BigDecimal 6935.00
class java.lang.Long 6935

您可以查看结果here