Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1480

IEEE Doubles: NaN, Infinity, etc.

$
0
0
When doing math that may have problems, I've traditionally resorted to Variants and returned a Null or Empty when things didn't go correctly. However, that's never felt totally clean. Lately, I've been relying on the NaN of an IEEE Double (and forgoing any use of Variants).

Basically, to summarize, I can think of five different "states" an IEEE Double may be in:
  • Zero
  • A typical floating point number.
  • A sub-normal floating point number.
  • A NaN
  • Infinity

And, there's also the sign-bit. However, the way IEEE Doubles are specified, the sign-bit is independent of all five of those "states". In other words, we can have -NaN or +NaN, -Inf, or +Inf. We can even have -0 or +0.

Also, just to quickly define them, the sub-normal numbers are numbers very close to zero. With the typical 11-bit exponent, this exponent can range from approximately 10+308 to 10-308. However, with a bit of trickery (i.e., using the mantissa as more exponent, and sacrificing mantissa precision), we can push on the negative exponent side, making it go to approximately 10-324 (the sub-normals). These sub-normal numbers are always very close to zero. I don't do anything special with these sub-normal numbers herein, but I just wanted to be complete.

Also, I list "Zero" separately from "A typical floating point number". This is because Zero is not handled (i.e., binary coded) the same way as other numbers. Zero just has all the bits off (with the possible exception of the sign bit).

Now, NaN is a special value that means "not-a-number". It's what you get when you try to divide 0#/0# (with error trapping turned on so you don't crash). There are also other ways to get it.

Infinity (or just Inf) is another one of these special values. You can get it by dividing any non-zero number by zero, such as 1#/0# (again, with error trapping).

There's a good Wikipedia page about these IEEE Doubles (which is just a Double type in VB6).

It's mostly these NaN and Inf values about which I post this entry. I've begun using them (instead of Variant) to handle special situations, and I thought I'd share. Also, the way I did things, there's no need for error trapping, which should keep things very fast.

Here's the code (possibly best in a BAS module):
Code:


Option Explicit
'
Public Declare Function GetMem2 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any) As Long ' Always ignore the returned value, it's useless.
Public Declare Function GetMem4 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any) As Long ' Always ignore the returned value, it's useless.
Public Declare Function GetMem8 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any) As Long ' Always ignore the returned value, it's useless.
'

Public Function NaN() As Double
    ' Math (add, subtract, multiply, divide) can be done on these, but nothing changes.
    ' They can NOT be used in "if NaN = NaN Then", or an overflow will result.  Use IsNaN().
    ' Also, most math-with-functions (Sin(), Round(), etc) causes overflow error.
    '
    GetMem2 &HFFF8, ByVal PtrAdd(VarPtr(NaN), 6&)
End Function

Public Function Inf() As Double
    GetMem2 &HFFF0, ByVal PtrAdd(VarPtr(Inf), 6&)
End Function

Public Function IsNaN(d As Double) As Boolean
    IsNaN = IsNanOrInf(d) And Not IsInf(d)
End Function

Public Function IsInf(d As Double) As Boolean
    Const ii As Integer = &H7FF0    ' High 4 bits of byte #7 (F0), Low 7 bits of byte #8 (7F). If all on, it's NaN (or Inf if all other non-sign bits are zero).
    Static i(1 To 4) As Integer
    GetMem8 d, i(1)
    IsInf = (i(4) And ii) = ii And i(1) = &H0 And i(2) = &H0 And i(3) = &H0 And (i(4) And &HF) = &H0
End Function

Public Function IsNeg(d As Double) As Boolean
    ' This works even on NaN and Inf.
    Static i(1 To 4) As Integer
    GetMem8 d, i(1)
    IsNeg = i(4) < 0    ' The sign bit will be the same sign bit for i(4).
End Function

Public Function IsNanOrInf(d As Double) As Boolean
    Const ii As Integer = &H7FF0    ' High 4 bits of byte #7 (F0), Low 7 bits of byte #8 (7F). If all on, it's NaN (or Inf if all other non-sign bits are zero).
    Static i(1 To 4) As Integer
    GetMem8 d, i(1)
    IsNanOrInf = (i(4) And ii) = ii
End Function

Public Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
    ' For adding (or subtracting) a small number from a pointer.
    ' Use PtrAddEx for adding (or subtracting) large numbers from a pointer.
    Const SIGN_BIT As Long = &H80000000
    PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
End Function

Just as an example of one place you may use these ... let's say you want to average a set of numbers. However, there may be cases where there are no numbers to average. What do you return? It's a problem, but returning a NaN can solve it so long as we remember to test for NaN before using it.

The following isn't complete code, but it's an example of where I'm using it. The caller then uses the IsNaN() function:

Code:


Private Function ParamSideAvg(iRow As Long, sSideLetter As String) As Double
    ' Returns NaN if nothing to average.
    Dim n As Double
    Dim iCnt As Long
    Dim iCol As Long
    '
    Select Case sSideLetter
    Case "L": iCol = ColNumberFromLetter("H")  ' This is the MEAN column.  Subtractions are made to get cycle data.
    Case "R": iCol = ColNumberFromLetter("N")  ' This is the MEAN column.  Subtractions are made to get cycle data.
    Case Else:  Exit Function
    End Select
    '
    If Len(Trim$(wsh.Cells(iRow, iCol - 3))) > 0 Then n = n + val(wsh.Cells(iRow, iCol - 3)): iCnt = iCnt + 1
    If Len(Trim$(wsh.Cells(iRow, iCol - 2))) > 0 Then n = n + val(wsh.Cells(iRow, iCol - 2)): iCnt = iCnt + 1
    If Len(Trim$(wsh.Cells(iRow, iCol - 1))) > 0 Then n = n + val(wsh.Cells(iRow, iCol - 1)): iCnt = iCnt + 1
    If iCnt > 0 Then
        ParamSideAvg = n / iCnt
    Else
        ParamSideAvg = NaN
    End If
End Function


Also, I suppose I could have also done all this for IEEE Singles, but I don't currently have the need.

Enjoy,
Elroy

Viewing all articles
Browse latest Browse all 1480

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>