Excel&VBA:“如果值小于1”,则由正好为1的值触发

我在Excel VBA中遇到了一个令人困惑的行为,我试图理解,并想知道是否有人可以指出我的解释方向吗?

背景 – 我已经inheritance了一个报告工具,用来计算每天是否有足够的剩余假期允许额外假期。 我发现,当余下的津贴正好是“1”时,performance出色。

下面是已经存在的VBA(实际文件中的variables的值由其他查询设置,但是我已经在这里手动设置它们来复制问题)。 使用此代码,即使(Total * Allowance) – 采取的结果恰好是1,并且只有小于1的值才能满足“If”条件,则会触发消息框

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 

我试着改变下面的代码,发现当'remaining'被声明为'double'数据types时,会出现同样的问题。 但是,如果我将“剩余”的数据types更改为“单一”,则代码将按预期方式运行,并且不会显示消息框:

 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在不同数据types中处理值“1”的方式有关,并且有些search出现了下面的文章,但是我不确定我是在正确的轨道还是错过了一个简单的答案 – 任何想法请?

第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 

生产:

在这里输入图像说明

这是一个已知和有据可查的“问题”与Excel。 总结如下: https : //en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel

“与其他电子表格一样,Microsoft Excel只能处理有限的准确性,因为它仅保留一定数量的数字来描述数字(精度有限)[…]虽然Excel可以显示30位小数,数量仅限于15位有效数字,由于三个问题(四舍五入,截断和二进制存储),计算的准确性可能会更低。

所以,如果你改变你的第二个代码片段如下:

 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

你是绝对正确的,数据types是问题。

问题是(基本上) Double不能准确地表示某些数字(尽pipe他们可以做得非常精确),因为浮点types如何编码它们所代表的数字的特性(基本上是:作为一个数字上升到一个幂) 。

如果你正在处理十进制数据或货币数据, 你可能最好使用固定的精度types,Currency ,而不是Double 精度 ,这将提供一个保证水平的精度(货币是精确和准确的4位小数)。

如果你需要比4个小数位更高的精度,使用一个Variant可能是你最好的select,并强迫它变成小数:

 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()会强制它)。