我在Excel VBA中遇到了令人困惑的行为,我试图理解并想知道是否有人能指出我的解释方向了吗?
背景 - 我继承了一个报告工具,用于计算每天是否有足够的剩余假期允许额外的假期。我发现当剩余的津贴恰好是' 1'时,它表现得出乎意料。
下面是已经存在的VBA(真实文件中的变量值是由其他查询设置的,但是我已经在这里手动设置它们以便复制问题)。使用此代码,即使(Total * Allowance) - Taken的结果正好为1并且' If'条件只能通过值小于1
来满足Dim Total As Double
Dim Allowance As Double
Dim Taken As Double
Total = 20
Allowance = 0.15
Taken = 2
If (Total * Allowance) - Taken < 1 Then
MsgBox "Not Enough Allowance Remaining"
End If
我尝试将代码更改为以下代码,并发现当剩下的时间为&#39;被宣布为&#39; double&#39;数据类型,出现同样的问题。但是,如果我更改了剩余的数据类型&#39;为了单个&#39;,代码按预期运行,并且不显示消息框:
Dim Total As Double
Dim Allowance As Double
Dim Taken As Double
Dim Remaining As Double
Total = 20
Allowance = 0.15
Taken = 2
Remaining = (Total * Allowance) - Taken
If Remaining < 1 Then
MsgBox "Not Enough Allowance Remaining"
End If
我认为它必须与Excel / VBA处理价值的方式有关&#39; 1&#39;在不同的数据类型和一些搜索中发现了下面的文章,但我不确定我是在正确的轨道上还是错过了一个更简单的答案 - 请问任何想法?
由于
答案 0 :(得分:1)
这是一个简单的舍入问题。这有效:
Sub dural()
Dim Total As Double
Dim Allowance As Double
Dim Taken As Double
Total = 20
Allowance = 0.15
Taken = 2
If ((Total * Allowance) - Taken) < 0.999999 Then
MsgBox "Not Enough Allowance Remaining"
End If
End Sub
因为浮点运算不能精确地产生 1
例如:
Sub dural()
Dim Total As Double
Dim Allowance As Double
Dim Taken As Double
Total = 20
Allowance = 0.15
Taken = 2
If (Total * Allowance) - Taken < 0.999999 Then
MsgBox "Not Enough Allowance Remaining"
End If
MsgBox 1 - ((Total * Allowance) - Taken)
End Sub
产地:
答案 1 :(得分:0)
这是一个已知且记录良好的问题&#34;用Excel。总结如下:https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel
&#34;与其他电子表格一样,Microsoft Excel的工作精度有限,因为它只保留一定数量的数字来描述数字(精度有限)。 [...]虽然Excel可以显示30个小数位,但它对指定数字的精度仅限于15个有效数字,并且由于三个问题,计算可能具有更低的精度:舍入,截断和二进制存储。 &#34;
因此,如果您将第二个代码段更改为以下内容:
Dim Total As Double
Dim Allowance As Double
Dim Taken As Double
Dim Remaining As Double
Total = 20
Allowance = 0.15
Taken = 2
Remaining = (Total * Allowance) - Taken
If Remaining < 1 Then
MsgBox 1 - Remaining
End If
您将意识到在15位小数处开始(完全如上所述)不准确性。如需更多样本和更精细的分析,您可能还需要查看以下帖子:http://www.cpearson.com/excel/rounding.htm
答案 2 :(得分:0)
绝对正确的是数据类型是个问题。
问题是(基本上)Double
不能准确地表示某些数字(尽管它们可以非常精确地表达),因为浮点类型如何编码它们所代表的数字的特殊性(基本上:as as一个被提升为权力的人。)
如果您正在处理十进制数据或货币数据,最好使用固定精度类型,例如Currency
,而不是Double
,这将提供有保证的精确度(货币精确且精确到4位小数)。
如果您需要比4位小数更高的精度,使用Variant
可能是您最好的选择,并将其强制转换为小数:
Dim Total As Variant
Dim Allowance As Variant
Dim Taken As Variant
Total = CDec(20)
Allowance = CDec(0.15)
Taken = CDec(2)
If (Total * Allowance) - Taken < 1 Then
MsgBox "Not Enough Allowance Remaining"
End If
(胁迫可能会隐含地发生,但如果你是偏执狂,CDec()
会强迫它。)