简而言之; 如果你需要比 Double
更高精度的数值,不要使用 Double
。
答案在于将结果从 Variant
转化为 Double
的时机。 Double
是一个IEEE 754浮点数,根据IEEE规范,其可逆性在15个有效数字内是有保障的。而你的值已经接近这个极限了:
0.5 * (152 * .784637) / (133 * .784637) * 70 = 39.99999999999997 (16 sig. digits)
当VBA强制转换为double时,超出15个有效数字的任何内容都会被四舍五入:
Debug.Print CDbl("39.99999999999997") '<--Prints 40
实际上,您可以在VBE中观察此行为。输入或复制以下代码:
Dim x As Double
x = 39.99999999999997
VBE会将文字值“自动修正”为Double
类型,所以你得到的是:
Dim x As Double
x = 40
好的,到目前为止你可能会问这与两个表达式之间的区别有什么关系。VBA使用“最高阶”变量类型来评估数学表达式。
在你的第二个Sub
中,你将所有变量声明为右侧的Double
,操作是使用Double
的高阶进行评估,然后结果在传递给Int()
参数之前隐式转换为Variant
。
在你的第一个Sub
中,你声明了Variant
,在传递给Int
之前不执行隐式转换-数学表达式中的最高阶是Variant
,因此在传递结果给Int()
之前不执行隐式转换 - Variant
仍然包含原始IEEE 754浮点数。
根据Int
的文档:
Int和Fix都会移除数字的小数部分并返回结果整数值。
不执行舍入。顶部代码调用Int(39.99999999999997)
。底部代码调用Int(40)
。答案取决于你想舍入到哪个浮点误差级别。如果15有效位数就可以,那么40就是“正确”的答案。如果你想将任何小于16个或更多有效数字的数字向下取整,则39是“正确”的答案。解决方案是使用Round
并明确指定你要查找的精度级别。例如,如果你关心全部15位数字:
Int(Round((0.5 * attack / defense * 70), 15))
请记住,在任何输入中使用的最高精度为6位数字,因此这将是一个逻辑舍入截止点:
Int(Round((0.5 * attack / defense * 70), 6))
0.5
和70
的顺序交换,你仍然可以得到 39:Int(70 * attack / defense * 0.5)
。 - ThunderFrame