Excel& VBA:'如果值小于1'条件由正好为1的值触发

时间:2016-05-12 13:54:23

标签: excel vba excel-vba

我在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;在不同的数据类型和一些搜索中发现了下面的文章,但我不确定我是在正确的轨道上还是错过了一个更简单的答案 - 请问任何想法?

Article 1
Article 2

由于

3 个答案:

答案 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

产地:

enter image description here

答案 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()会强迫它。)