使用VBA我將8字節的浮點數從字節數組加載到Double中。某些數字將是IEEE 754 NaN(即如果您嘗試使用Debug.Print打印它,您將看到1.#QNAN)。我的問題是,如何測試Double中包含的數據是否是NaN而不是常規數字?在VBA/VB6中測試NaN
謝謝。
使用VBA我將8字節的浮點數從字節數組加載到Double中。某些數字將是IEEE 754 NaN(即如果您嘗試使用Debug.Print打印它,您將看到1.#QNAN)。我的問題是,如何測試Double中包含的數據是否是NaN而不是常規數字?在VBA/VB6中測試NaN
謝謝。
NaN在指數中有一個模式,可以在它們仍然在字節數組中時識別。具體來說,任何NaN都將具有全1的指數,就像您可能應該陷入的任何Infinity一樣。
在雙,指數是在最上位的兩個字節:
SEEEEEEE EEEEMMMM MMM....
假設那些是b(0)和b(1):
Is_A_Nan = ((b(0) And &H7F) = &H7F) And ((b(1) And &HF0) = &HF0)
這是空氣代碼,但你明白了。
如果您需要區分SNaN,QNaN和Infinity,則需要更深入一點,但聽起來不像是您的問題。
我應該注意如果字節是相反的順序,用b(6)代替b(1),用b(7)代替b(0)... – 2010-04-28 17:03:35
謝謝Jim,這個工作很完美。我還用一個4字節的Single進行了測試,在這種情況下,它似乎只需要測試第一個字節。 – Abiel 2010-04-28 20:06:40
關於單身:不完全。第二個字節測試將變爲 ((b(1)And&H80)=&H80) – 2010-04-29 11:39:43
您可以通過其十六進制值分配給兩個32位多頭,然後將值複製到雙用CopyMemory的
Public Declare Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (destination As Any, source As Any, _
ByVal length As Long)
Public Function QNaN() As Double
Dim Oput As Double
Dim l(1 To 2) As Long
l(1) = &H7FFFFFFF
l(2) = &HFFFFFFFF
CopyMemory Oput, l(1), 8
QNaN = Oput
End Function
OP需要測試NaN,而不是生成一個。 – GSerg 2016-03-05 15:57:38
這裏產生一個雙QNAN是功能測試所有特殊值一套房: qnans溢出,無限。把整個代碼塊放在一個模塊中,你應該很好走。
Option Explicit
Public Declare Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (destination As Any, source As Any, _
ByVal length As Long)
'***************************************************************
'Test to see if the functions work
'**************************************************************
Public Sub Test()
'This tests the functions above against a set of doubles
'note that this is not an exhaustive test since there are
'18,014,398,509,481,984 special bit patterns. We test 7 of them
'This test assumes that ThisWorkbook has a sheet with code name Sheet1
Dim l(1 To 2) As Long, Vals(1 To 8) As Double, Oput As Variant
Dim Num As Long
'generate values to test
DoubleFromHex &HFFF00000, 1, Vals(1) 'negative overflow
DoubleFromHex &H7FF00000, 1, Vals(2) 'positive overflow
DoubleFromHex &H7FF80000, 0, Vals(3) 'Positive QNaN
DoubleFromHex &HFFF80000, 0, Vals(4) 'Indeterminate
DoubleFromHex &HFFF80000, 1, Vals(5) 'Negative QNaN
DoubleFromHex &H7FF00000, 0, Vals(6) 'Pos Infinity
DoubleFromHex &HFFF00000, 0, Vals(7) 'Neg Infinity
Vals(8) = 2.35345246654325E+27 'actual number generated using number pad fist mash alogorithm
'dimension output
ReDim Oput(1 To UBound(Vals) + 1, 1 To UBound(Vals) + 1)
'fill test titles
Oput(1, 2) = "IsOverflow"
Oput(1, 3) = "IsPosQNaN"
Oput(1, 4) = "IsNegQNaN"
Oput(1, 5) = "IsIndetermiate"
Oput(1, 6) = "IsPosInfinity"
Oput(1, 7) = "IsNegInfinity"
Oput(1, 8) = "IsSpecial"
'fill number titles
Oput(2, 1) = "Negative Overflow"
Oput(3, 1) = "Positive Overflow"
Oput(4, 1) = "Positive QNaN"
Oput(5, 1) = "Indeterminate"
Oput(6, 1) = "Negative QNaN"
Oput(7, 1) = "Pos Infinity"
Oput(8, 1) = "Neg Infinity"
Oput(9, 1) = "Actual number"
'perform tests
For Num = 1 To 8
Oput(Num + 1, 2) = IsOverflow(Vals(Num))
Oput(Num + 1, 3) = IsPosQNaN(Vals(Num))
Oput(Num + 1, 4) = IsNegQNaN(Vals(Num))
Oput(Num + 1, 5) = IsIndetermiate(Vals(Num))
Oput(Num + 1, 6) = IsPosInfinity(Vals(Num))
Oput(Num + 1, 7) = IsNegInfinity(Vals(Num))
Oput(Num + 1, 8) = IsSpecial(Vals(Num))
Next Num
'put to sheet
Sheet1.Range("A1").Resize(UBound(Oput), UBound(Oput, 2)).Value = Oput
End Sub
'***************************************************************
'Functions
'**************************************************************
Public Function IsOverflow(Val As Double) As Boolean
'This function returns true for doubles that VBA recognises as
'<overflow>
'it returns false for any other doubles
'Doubles represented by <overflow> in VBA are more commonly known
'as signalling NaNs
Dim l(1 To 2) As Double
'eliminate the positive and negative infinity
If IsPosInfinity(Val) Then Exit Function
If IsNegInfinity(Val) Then Exit Function
'Convert the 64 bit double to 2 longs represented as doubles
DeconstructDouble l, Val
'test for positive overflow
If l(2) >= USig(&H7FF00000) And l(2) <= USig(&H7FF7FFFF) Then
IsOverflow = True
ElseIf l(2) >= USig(&HFFF00000) And l(2) <= USig(&HFFF7FFFF) Then
'test for negative overflow
IsOverflow = True
End If
End Function
Public Function IsPosQNaN(Val As Double) As Boolean
'This function returns true for doubles that VBA recognises as
'1.#QNAN (quiet not a number)
'it returns false for any other doubles
Dim l(1 To 2) As Double
'Convert the 64 bit double to 2 longs represented as doubles
DeconstructDouble l, Val
'test for positive QNaN
IsPosQNaN = (l(2) >= USig(&H7FF80000)) And (l(2) <= USig(&H7FFFFFFF))
End Function
Public Function IsNegQNaN(Val As Double) As Boolean
'This function returns true for doubles that VBA recognises as
'-1.#QNAN (negative quiet not a number)
'it returns false for any other doubles
Dim l(1 To 2) As Double
'Convert the 64 bit double to 2 longs represented as doubles
DeconstructDouble l, Val
'test for negative QNaN
IsNegQNaN = (l(2) >= USig(&HFFF80000)) And (l(1) <> 0)
End Function
Public Function IsIndetermiate(Val As Double) As Boolean
'This function returns true for doubles that VBA recognises as
' -1.#IND (indeterminate)
'it returns false for any other doubles
Dim l(1 To 2) As Long
'Convert the 64 bit double to 2 longs
CopyMemory l(1), Val, 8
'test for indeterminate
IsIndetermiate = (l(2) = &HFFF80000) And ((l(1) = 0))
End Function
Public Function IsPosInfinity(Val As Double) As Boolean
'returns true if and only if Val is recognised by VBA as 1.#INF
Dim l(1 To 2) As Long
'Convert the 64 bit double to 2 longs
CopyMemory l(1), Val, 8
'Check for negative infinity
IsPosInfinity = (l(1) = 0) And (l(2) = &H7FF00000)
End Function
Public Function IsNegInfinity(Val As Double) As Boolean
'returns true if and only if Val is recognised by VBA as -1.#INF
Dim l(1 To 2) As Long
'Convert the 64 bit double to 2 longs
CopyMemory l(1), Val, 8
'Check for negative infinity
IsNegInfinity = (l(1) = 0) And (l(2) = &HFFF00000)
End Function
Public Function IsSpecial(Val As Double) As Boolean
'returns true if Val is represented by VBA as any of
'1.#INF,-1.#INF,-1.#IND,-1.#QNAN,1.#QNAN,<overflow>
'ie returns true if and only if any of the other functions return true
Dim l(1 To 2) As Double
'Convert the 64 bit double to 2 longs represented as doubles
DeconstructDouble l, Val
IsSpecial = ((l(2) >= USig(&H7FF00000)) And (l(2) < USig(&H80000000))) Or l(2) >= USig(&HFFF00000)
End Function
'****************************************************
'Utility Functions
'****************************************************
Private Sub DoubleFromHex(Part1 As Long, Part2 As Long, Oput As Double)
'convert a hex representation of a double into a double
'can be used to generate doubles otherwise inaccessible by vba
Dim l(1 To 2) As Long
l(1) = Part2
l(2) = Part1
CopyMemory Oput, l(1), 8
End Sub
Private Function USig(l As Long) As Double
'returns an unsigned value of a long as as double
If l < 0 Then
USig = 4294967296# + l
Else
USig = l
End If
End Function
Private Sub DeconstructDouble(Oput() As Double, Iput As Double)
'Splits the double's binary representation into 2 unsigned longs represented as doubles
Dim l(1 To 2) As Long
CopyMemory l(1), Iput, 8
Oput(1) = USig(l(1))
Oput(2) = USig(l(2))
End Sub
我發現最簡單的方法是簡單地將該值更改爲字符串,並檢查它是否等於1.#QNAN。我從來沒有遇到過不同類型的NaN,但是你總是可以將它擴展到你的NaN值的字符串值。
Function IsQNaN(number As Double) As Boolean
If CStr(number) = "1.#QNAN" Then
IsQNAN = True
Else
IsQNaN = False
End If
End Function
參見本http://stackoverflow.com/questions/885994/how-do-you-get-vb6-to-initialize-doubles-with-infinity-infinity-and-nan – MarkJ 2011-05-20 11:23:43