问题描述
有人可以向我解释为什么 ColdFusion(在 2016、2018 和 2021 年测试过)进行了错误的双到长转换?我知道它可能会弄乱小数值,但在这个例子中,它显然是一个整数值。
这是代码:
<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 是在 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 ......这是无类型编程语言的“好”副作用之一,它在内部使它。让我想起了 javascript ;-)
编辑 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 * 100
是 6934.999999999999
(如果您愿意,请检查 JS、Python、Ruby 等),因为在一种以二进制形式存储东西的系统。我以前写过关于这个:Floating point arithmetic with decimals。
ColdFusion 在内部将结果存储为 6934.999999999999
:
<cfset f = 69.35 * 100>
<cfoutput>#f.toString()#</cfoutput>
这产生:
6934.999999999999
因此,当您使用 int
取 6934.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。