Jump to content
Bay Six Software Forum Index Bay Six Software
Beyond the Basics
 
 FAQFAQ   SearchSearch   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

ULong may be used as a "float" device
 
By Brent D. Thorn

Liberty BASIC 4 provides us with some great facilities for calling external code written in another language, such as C, if the non-LB code is compiled to a standard DLL. LB can pass these functions 16- and 32-bit integers (signed or unsigned), 64-bit (double-precision) real numbers, pointers to the same, and strings and structs.

The Problem

Where LB fails us is in calls using a 32-bit (single-precision) real number. C-derived languages call this a "float". While the Windows API uses very few FP data types, many third-party DLLs use them, and some use them a lot.

Some inexperienced LB-ers, upon encountering "float", simply regard it as a synonym for "double" and translate it into their LB code as such. Inexplicably, sometimes this works, but much more often the program fails--sometimes spectacularly.

The Solution

The "oleaut32" DLL that ships with Windows provides many functions for converting one data type to another. Two of these functions--"VarR4FromR8" and "VarR8FromR4"--look like they will fill the bill. The former converts a double to a float, and the latter converts it back to double.

The reader is probably scratching his or her head right now. "If LB doesn't have a single-precision type, how does a function that returns that type help?," you might ask.

As stated above, a float is 32 bits (4 bytes) in width. This is the same size as a "long" or "ulong" in LB. We can "pretend" these types are the same and use ulong in our LB code wherever a float is required, then use the conversion functions to give LB a type it can use.

The conversion functions are wrapped in two easy-to-use LB functions below.
Code:
Function toFloat( R8 )
'-- Converts a 64-bit Double to a 32-bit number.
    Struct local1, R4 As ULong
    CallDLL #oleaut32, "VarR4FromR8", _
        R8 As Double, local1 As Struct, _
        ret As Long
    toFloat = local1.R4.struct
End Function

Function fromFloat( R4 )
'-- Converts a 32-bit number to a 64-bit Double.
    Struct local1, R8 As Double
    CallDLL #oleaut32, "VarR8FromR4", _
        R4 As ULong, local1 As Struct, _
        ret As Long
    fromFloat = local1.R8.struct
End Function


Step by Step

  1. Copy the wrapper functions and paste them to the end of your LB code.
  2. In your initialization code, open "oleaut32.dll" as #oleaut32.
  3. In your shutdown code, close #oleaut32.
  4. Anywhere a float is needed (function call or struct), use a ulong.
  5. In a CallDLL, make sure you are passing a variable because a numeric literal won't work.
  6. When a float needs to be passed to a function or assigned to a struct element, convert the value to pseudo-float/ulong by wrapping the value in a call to toFloat.
  7. When a function returns a float or modifies a struct with float, convert the value to true floating-point by wrapping the value in a call to fromFloat.


Simple Example

The following code demonstrates the conversion process. It calculates the value of pi, which is a type double. Then it converts it to an integer containing the same bits a float would. Finally, the value is converted to a double, but retains the same precision as a float.
Code:
    Open "oleaut32" For DLL As #oleaut32

    dblPI = 4 * Atn(1)
    lngPI = toFloat(dblPI)
    sngPI = fromFloat(lngPI)

    Print "dblPI = ";dblPI
    Print "lngPI = ";DecHex$(lngPI)
    Print "sngPI = ";sngPI

    Close #oleaut32

    End

Powered by phpBB © 2001, 2005 phpBB Group