我正在使用Excel 2013。在下面的代码片段中,VBA计算damage为40
Dim attack As Variant, defense As Variant, damage As Long
attack = 152 * 0.784637
defense = 133 * 0.784637
damage = Int(0.5 * attack / defense * 70)如果数据类型更改为Double,则VBA为damage计算39
Dim attack As Double, defense As Double, damage As Long
attack = 152 * 0.784637
defense = 133 * 0.784637
damage = Int(0.5 * attack / defense * 70)在调试器中,Variant/Double和Double值显示相同。然而,Variant/Double似乎具有更高的精确度。
有人能解释这种行为吗?
发布于 2017-01-30 12:50:05
tldr;如果您需要比Double更高的精度,请不要使用Double。
答案在于何时将结果从Variant强制到Double中的时间。Double是一个IEEE754浮点数,根据IEEE规范,可逆性保证为15位有效数字。你的价值接近这个极限:
0.5 * (152 * .784637) / (133 * .784637) * 70 = 39.99999999999997 (16 sig. digits)当VBA被强制为双精度时,它将舍入超过15位有效数字的任何数:
Debug.Print CDbl("39.99999999999997") '<--Prints 40事实上,您可以在VBE中此行为。键入或复制以下代码:
Dim x As Double
x = 39.99999999999997VBE通过将其转换为Double来“自动更正”文字值,这将为您提供:
Dim x As Double
x = 40#到目前为止,你可能会问,这和两个表达式之间的区别有什么关系。VBA尽可能使用“最高阶”变量类型来计算数学表达式。
在您的第二个Sub中,您在右侧将所有变量声明为Double,使用Double的高位计算操作,然后将结果隐式转换为Variant,然后作为Int()的参数传递。
在您拥有Variant声明的第一个Sub中,在传递给Int之前不会执行对Variant的隐式强制转换-数学表达式中的最高阶是Variant,因此在将结果传递给Int()之前不会执行任何隐式强制转换- Variant仍然包含原始的IEEE754浮点数。
根据Int的documentation
和Fix都删除number的小数部分并返回结果整数值。
不执行舍入。最上面的代码调用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))发布于 2017-01-30 12:12:43
如果在计算损害的两行中去掉Int()函数,那么两行的结果都是相同的。你不应该使用Int,因为这会产生错误的行为,你应该使用CLng,因为你正在转换为一个长变量,或者如果damage是Int,你应该使用CInt。
Int和CInt的行为不同。Int总是向下舍入到下一个较低的整数-而CInt将使用银行家的舍入进行向上或向下舍入。对于尾数为0.5的数字,您通常会看到这种行为。
至于variant和double差异,如果你对第一个代码块的MsgBox执行TypeName,你会发现在分配了值之后,攻击和防御都被转换成了double,尽管已经声明为variant。
https://stackoverflow.com/questions/41928378
复制相似问题