我正在使用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
似乎更精确。
任何人都可以解释这种行为吗?
答案 0 :(得分:5)
tldr; 如果您需要的精度高于Double
,请不要使用Double
。
答案在于将结果从Double
强制转换为Variant
的时间。 Double
是IEEE 754浮点数,根据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.99999999999997
VBE&#34;自动更正&#34;通过将其转换为Double
的字面值,它可以为您提供:
Dim x As Double
x = 40#
好的,所以现在你可能会问这与2个表达式之间的区别有什么关系。 VBA使用&#34;最高阶&#34;来评估数学表达式。它可以变量类型。
在您的第二个Sub
中,右侧的所有变量都声明为Double
,操作将按Double
的高阶进行评估,然后是结果在作为Variant
的参数传递之前隐式转换为Int()
。
在您Sub
声明的第一个Variant
中,在传递给Variant
之前,Int
的隐式强制转换不会被执行 - 数学表达式为Variant
,因此 在将结果传递给Int()
之前不执行隐式强制转换 - Variant
仍然包含原始数据IEEE 754浮动。
根据Int
的{{3}}:
Int和Fix都会删除数字的小数部分并返回 结果整数值。
不执行舍入。顶部代码调用Int(39.99999999999997)
。底部代码调用Int(40)
。 &#34;答案&#34;取决于您想要舍入的浮点错误级别。如果15有效,那么40就是&#34;正确&#34;回答。如果你想要对16位或更多有效数字进行处理,那么39就是&#34;正确的&#34;回答。 解决方案将使用Round
并指定您要明确查找的精度级别。例如,如果您关心完整的15位数字:
Int(Round((0.5 * attack / defense * 70), 15))
请记住,您在输入中的任何位置使用的最高精度是6位数,因此这将是一个逻辑舍入截止:
Int(Round((0.5 * attack / defense * 70), 6))
答案 1 :(得分:0)
如果你在计算损伤的两条线上摆脱了Int()函数,最终都是相同的。你不应该使用Int,因为这会产生错误的行为,你应该在转换为Long变量时使用CLng,或者如果损坏是Int,你应该使用CInt。
Int和CInt表现不同。 Int总是向下舍入到下一个较低的整数 - 而CInt将使用Banker的Rounding向上或向下舍入。对于尾数为0.5的数字,您通常会看到此行为。
对于变量和双重差异,如果对第一个代码块对MsgBox执行TypeName,您会发现在分配了值之后攻击和防御都已转换为double,尽管已声明作为变种。