VBA:Variant / Double和Double之间的区别

时间:2017-01-30 02:45:53

标签: vba double precision variant

我正在使用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/DoubleDouble值显示相同。但是,Variant/Double似乎更精确。

任何人都可以解释这种行为吗?

2 个答案:

答案 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,尽管已声明作为变种。