问题描述
|
在某些迭代优化过程中,以下用于计算双变量正态CDF的VBA代码有时在上层函数的
while
循环内的z = hx * hy * c
的行上引发溢出错误。
我调试了代码,当数字相乘得出的数字大于双精度数可以容纳的数字时,就会发生溢出。
您能否通过忽略如此高的值的循环迭代来演示如何处理该问题-我想这是唯一可行的解决方案(?)。我在乘法之前用On Error Goto nextiteration行尝试了一下,并将nextiteration跳转点放在Wend之前,但是错误仍然存在。
Function tetrachoric(x As Double,y As Double,rho As Double) As Double
Const FACCURACY As Double = 0.0000000000001
Const MinStopK As Integer = 20
Dim k As Integer
Dim c As Double
Dim z As Double
Dim s As Double
Dim hx As Double
Dim hx1 As Double
Dim hx2 As Double
Dim hy As Double
Dim hy1 As Double
Dim hy2 As Double
Dim CheckPass As Integer
hx = 1
hy = 1
hx1 = 0
hy1 = 0
k = 0
c = rho
z = c
s = z
CheckPass = 0
While CheckPass < MinStopK
k = k + 1
hx2 = hx1
hy2 = hy1
hx1 = hx
hy1 = hy
hx = x * hx1 - (k - 1) * hx2
hy = y * hy1 - (k - 1) * hy2
c = c * rho / (k + 1)
z = hx * hy * c
s = s + z
If Abs(z / s) < FACCURACY Then
CheckPass = CheckPass + 1
Else
CheckPass = 0
End If
Wend
tetrachoric = s
End Function
Public Function bivnor(x As Double,rho As Double) As Double
\'
\' bivnor function
\' Calculates bivariat normal CDF F(x,y,rho) for a pair of standard normal
\' random variables with correlation RHO
\'
If rho = 0 Then
bivnor = Application.WorksheetFunction.normSdist(x) * _
Application.WorksheetFunction.normSdist(y)
Else
bivnor = Application.WorksheetFunction.normSdist(x) * _
Application.WorksheetFunction.normSdist(y) + _
Application.WorksheetFunction.normdist(x,1,False) * _
Application.WorksheetFunction.normdist(y,False) * _
tetrachoric(x,rho)
End If
End Function
来源:可从http://michael.marginalq.com/下载
解决方法
您正在达到计算机体系结构的极限。由于性能原因和/或溢出时的错误行为,许多复杂的算法无法以其数学表示形式一对一地实现。关于这些问题,有一个非常出色的博客-John D. Cook。
请在此处查看以获得更好的实现。
您还可以尝试绑定外部库,从而为您提供任意精度的数字处理,当然可以使用非常昂贵的(就CPU时间而言)软件算法来实现。在这里可以找到更多。
,使用
On Error Resume Next
而不是On Error Goto
更新了代码:
While CheckPass < MinStopK
k = k + 1
hx2 = hx1
hy2 = hy1
hx1 = hx
hy1 = hy
hx = x * hx1 - (k - 1) * hx2
hy = y * hy1 - (k - 1) * hy2
c = c * rho / (k + 1)
On Error Resume Next
z = hx * hy * c
If Err.Number = 0 Then
s = s + z
If Abs(z / s) < FACCURACY Then
CheckPass = CheckPass + 1
Else
CheckPass = 0
End If
Else
Err.Clear
End If
Wend
,http://www.codeproject.com/KB/recipes/float_point.aspx处理如何“使用对数避免溢出和下溢”,这是解决溢出问题的一种简单但非常有效的方法。实际上,它是如此简单而又合乎逻辑,为什么我们自己还没有想到该解决方案? ;)