【问题标题】:Why does a for-loop in VBA loop one less time when using bounds in the ten-thousandths place?为什么在万分之一处使用边界时,VBA 中的 for 循环会少循环一次?
【发布时间】:2014-01-17 21:17:47
【问题描述】:

在 VBA 中处理函数时,我注意到有时在测试过程中,我会得到与使用不同参数运行相同函数时不同的结果。我写了下面的 sub 试图弄清楚发生了什么。

for-loop 以千分之一的位置执行时,它会循环执行for 语句3 次,正如我所期望的那样。在这里,即时窗口为我慷慨地打印了一个“3”:

Const vStart = 0.001
Const vStep = 0.001
Const vStop = 0.003

Sub LoopThru()
    Dim myVar As Double
    Dim counter As Integer 

    For myVar = vStart To vStop Step vStep
        counter = counter + 1
    Next myVar

    Debug.Print counter
End Sub

但是,当我将界限和步数减小一个数量级时,for-loop 只会执行两次 for 语句。

Const vStart = 0.0001
Const vStep = 0.0001
Const vStop = 0.0003

Sub LoopThru()
    Dim myVar As Double
    Dim counter As Integer

    For myVar = vStart To vStop Step vStep
        counter = counter + 1
    Next myVar

    Debug.Print counter
End Sub

在我的实际项目中,我正在创建一个大型 3D 值数组作为查找表。由于必须使用 Solver 生成数组的第一个维度,因此我将通过创建此查找表来大大减少应用程序的运行时间,而不必在应用程序运行时一遍又一遍地使用 Solver 重新计算。

由于是 3D 数组,所以有 3 个嵌套的 for 循环,其中第二个的取值范围可能是从百分之一到十分之一。

也许do-while loop 更适合这种情况——事实上,我现在正在重写代码以改用while 循环。或for-loops 的条件集。无论哪种方式,我想知道这个问题的最佳解决方案,以及为什么执行的循环数会根据这些for loops 中的边界大小而有所不同。

【问题讨论】:

  • 如果你明确声明常量'As Double'会发生什么?我正在考虑在循环中从隐式初始化类型转换或四舍五入到 Double 可能会在某个地方出错。
  • For 循环出现问题的原因是浮点精度(或缺乏)。 See this 进行解释。您需要为循环计数器使用 Long 类型。
  • @Eight-BitGuru,我尝试将常量声明为Doubles,但无济于事。 @chris,我也曾尝试将循环计数器 (myVar) 声明为 Long,但 Long 变量类型是整数,所以我刚刚溢出。帮助菜单还让我打开了变量类型vbDecimal,我尝试过,但也没有用。也许缺少一个包?
  • 问题的根源在于浮点精度,以及由此产生的舍入。您需要将循环计数器响应Long 更改为不同的浮点数据类型并不能解决根本问题。如果您需要这方面的帮助,请在您的 Q 中添加更多详细信息。

标签: vba excel for-loop decimal


【解决方案1】:

原因是可以存储在浮点变量中的精度有限。 要获得完整的解释,您应该阅读 David Goldberg 发表在 1991 年 3 月的 Computing Surveys 上的论文“What Every Computer Scientist Should Know About Floating-Point Arithmetic”

Link to paper

在 VBA 中,默认浮点类型是 Double,它是一个 IEEE 64 位(8 字节)浮点数。

您需要重构代码以在循环计数器中使用整数类型。

要演示一种转换为整数循环计数器的方法,请考虑以下内容:

Const vStart = 0.0001
Const vStep = 0.0001
Const vStop = 0.0003

Sub LoopThru()
    Dim myVar As Double
    Dim counter As Long

    For counter = 0 To (vStop - vStart) * 10000 Step vStep * 10000
        myVar = counter * vStep + vStart
        Debug.Print "Iteration " & counter, "myVar = " & myVar
    Next
End Sub

您需要如何在更广泛的环境中实施取决于您的具体情况。

既然你提到你正在循环数组,请考虑使用类似

For i = LBound(YourArray, n) to UBound(YourArray, n)

其中n = 要循环的数组维度

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2011-05-11
    • 1970-01-01
    • 2017-09-20
    • 2016-01-09
    • 2017-10-13
    相关资源
    最近更新 更多