|
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
- Copy the wrapper functions and paste them to the end of your LB code.
- In your initialization code, open "oleaut32.dll" as #oleaut32.
- In your shutdown code, close #oleaut32.
- Anywhere a float is needed (function call or struct), use a ulong.
- In a CallDLL, make sure you are passing a variable because a numeric literal won't work.
- 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.
- 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 |
|
|
|