如何使VB6能够使用+无穷大、-无穷大和NaN来初始化double类型变量?

9

似乎VB6不太容易将+无限大、-无限大和NaN存储到double变量中。如果可以这样做,我可以在复数的上下文中使用这些值进行比较。该如何实现?

3个回答

20

实际上,有一种更简单的方法可以获得无穷大、负无穷大和非数值:

public lfNaN    as Double ' or As Single
public lfPosInf as Double
public lfNegInf as Double

on error resume next    ' to ignore Run-time error '6': Overflow and '11': Division by zero
lfNaN    =  0 / 0       ' -1.#IND
lfPosInf =  1 / 0       '  1.#INF
lfNegInf = -1 / 0       ' -1.#INF

on error goto 0         ' optional to reset the error handler

1
+1 我以前从未知道这个!看起来你也可以通过计算0/0来得到NAN?无论如何,Debug.Print显示为-1.#IND,这与我从1/0中得到的1.#INF不同。我认为它是NAN。 - MarkJ
1
Debug.Print -lfNaN会返回1.#QNAN,我猜这是“quiet(安静的)”NaN吧(?)。 - Andre

15

有几件不同的事情。就像你从Pax的例子中看到的那样,你只需要查找IEEE 754标准,然后将字节放入正确的位置即可。我唯一要提醒你的是,微软已经弃用RtlMoveMemory,因为它可能会创建溢出类型的安全问题。作为替代方案,你可以使用用户定义类型和LSet在“纯”VB中进行小心的强制转换实现这一点。(还请注意,有两种类型的NaN。)

Option Explicit

Public Enum abIEEE754SpecialValues
    abInfinityPos
    abInfinityNeg
    abNaNQuiet
    abNaNSignalling
    abDoubleMax
    abDoubleMin
End Enum

Private Type TypedDouble
    value As Double
End Type

Private Type ByteDouble
    value(7) As Byte
End Type

Public Sub Example()
    MsgBox GetIEEE754SpecialValue(abDoubleMax)
End Sub

Public Function GetIEEE754SpecialValue(ByVal value As abIEEE754SpecialValues) As Double
    Dim dblRtnVal As Double
    Select Case value
    Case abIEEE754SpecialValues.abInfinityPos
        dblRtnVal = BuildDouble(byt6:=240, byt7:=127)
    Case abIEEE754SpecialValues.abInfinityNeg
        dblRtnVal = BuildDouble(byt6:=240, byt7:=255)
    Case abIEEE754SpecialValues.abNaNQuiet
        dblRtnVal = BuildDouble(byt6:=255, byt7:=255)
    Case abIEEE754SpecialValues.abNaNSignalling
        dblRtnVal = BuildDouble(byt6:=248, byt7:=255)
    Case abIEEE754SpecialValues.abDoubleMax
        dblRtnVal = BuildDouble(255, 255, 255, 255, 255, 255, 239, 127)
    Case abIEEE754SpecialValues.abDoubleMin
        dblRtnVal = BuildDouble(255, 255, 255, 255, 255, 255, 239, 255)
    End Select
    GetIEEE754SpecialValue = dblRtnVal
End Function

Public Function BuildDouble( _
    Optional byt0 As Byte = 0, _
    Optional byt1 As Byte = 0, _
    Optional byt2 As Byte = 0, _
    Optional byt3 As Byte = 0, _
    Optional byt4 As Byte = 0, _
    Optional byt5 As Byte = 0, _
    Optional byt6 As Byte = 0, _
    Optional byt7 As Byte = 0 _
    ) As Double
    Dim bdTmp As ByteDouble, tdRtnVal As TypedDouble
    bdTmp.value(0) = byt0
    bdTmp.value(1) = byt1
    bdTmp.value(2) = byt2
    bdTmp.value(3) = byt3
    bdTmp.value(4) = byt4
    bdTmp.value(5) = byt5
    bdTmp.value(6) = byt6
    bdTmp.value(7) = byt7
    LSet tdRtnVal = bdTmp
    BuildDouble = tdRtnVal.value
End Function

最后一点需要注意的是,你也可以通过这种方式得到NaN:

Public Function GetNaN() As Double
    On Error Resume Next
    GetNaN = 0 / 0
End Function

这是非常棒的东西。非常感谢你分享它。 - bugmagnet
我意识到这个答案已经有13年的历史了,但是关于信号NaN的位模式是错误的。第51位是“QUIET BIT”。因此,对于“abNaNSignalling”,“byt6”必须为“240”。然后,还必须在尾数中设置一个或多个位。 - Excel Hero

4

这个页面展示了一种略微复杂的方法来实现它。我已经简化了它以匹配您的问题,但还没有进行彻底测试。如果有任何问题,请告诉我。我注意到该网站上的一个问题是,他们用于静默NaN的代码是错误的,应该使用1位来启动尾数 - 他们似乎将其与信号NaN混淆了。

Public NegInfinity As Double
Public PosInfinity As Double
Public QuietNAN As Double

Private Declare Sub CopyMemoryWrite Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByVal Destination As Long, source As Any, ByVal Length As Long)

' IEEE754 doubles:                                                          '
'   seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm '
'   s = sign                                                                '
'   e = exponent                                                            '
'   m = mantissa                                                            '
'   Quiet NaN: s = x, e = all 1s, m = 1xxx...                               '
'   +Inf     : s = 0, e = all 1s, m = all 0s.                               '
'   -Inf     : s = 1, e = all 1s, m = all 0s.                               '

 

Public Sub Init()
    Dim ptrToDouble As Long
    Dim byteArray(7) As Byte
    Dim i As Integer

    byteArray(7) = &H7F
    For i = 0 To 6
        byteArray(i) = &HFF
    Next
    ptrToDouble = VarPtr(QuietNAN)
    CopyMemoryWrite ptrToDouble, byteArray(0), 8

    byteArray(7) = &H7F
    byteArray(6) = &HF0
    For i = 0 To 5
        byteArray(i) = 0
    Next
    ptrToDouble = VarPtr(PosInfinity)
    CopyMemoryWrite ptrToDouble, byteArray(0), 8

    byteArray(7) = &HFF
    byteArray(6) = &HF0
    For i = 0 To 5
        byteArray(i) = 0
    Next
    ptrToDouble = VarPtr(NegInfinity)
    CopyMemoryWrite ptrToDouble, byteArray(0), 8
End Sub

它基本上使用内核级内存复制将位模式从字节数组传输到双精度浮点数。

然而,您需要记住,有多个比特值可以表示QNaN,具体来说,符号位可以是0或1,除第一个位以外的所有尾数位也可以是零或1。这可能会使您的比较策略变得复杂,除非您可以发现VB6只使用其中一种位模式-但这不会影响这些值的初始化,假设VB6正确实现了IEE754双精度浮点数。


那么你正在链接到原问题提问者的博客,他在提问前一天发布了一个帖子,展示了他最好的尝试?很公平,这有点有趣! - MarkJ
这不仅仅是有趣,而是非常滑稽。当时我实际上并不知道提问者是那个博客的所有者,但他的stackoverflow昵称就在博客上 :-) 我对是否删除这个答案犹豫不决。如果没有其他的,它可能会给其他人带来一些乐趣。 - paxdiablo
我不确定是该笑还是该尴尬。 - bugmagnet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接