Converting Between Single Precision and Double Precision Floats In Basic
The Armmites and Picomites use double precision floats. When interfacing some devices they will return the 32 bits that represent a single precision float. In the Micromites which use single precision you can just POKE the 32 bits into the memory occupied by a float and then access the value by using the variable.
The MMBasic devices using double precision expect you to POKE 64 bits, but you only have 32.
This TBS post by @matherp was used as the basis for this article and details how to manipulate the bits to convert from single to double precision.
Functions in MMBasic
Function to convert between single and double precision
These functions manipulate the bits in a stored float to go between single and double precision float as required. They are contributed by various TBS members and are extracted from the TBS thread.
'================================================================================================
' Configure platform specific variables
'================================================================================================
DIM integer doubleprecision
if (MM.DEVICE$="Micromite MkII" OR MM.DEVICE$="Micromite Plus") THEN
doubleprecision=0
else
doubleprecision=1
ENDIF
'---------------------------------------------------------------------------------
'This function takes a float (single or double precision depending on the platform)
'and returns an integer where the last 32 bits contain binary formatted as for a
'32 bit float.(This could then be sent to a device that expects single precision).
'---------------------------------------------------------------------------------
Function FloatToBIN32(i As FLOAT) As INTEGER
local INTEGER w%,c%,d%,e%,o%
Local INTEGER j
if doubleprecision=0 then
FloatToBIN32=Peek(INTEGER Peek(VARADDR i))
else
if i<>0 then
Poke FLOAT Peek(varaddr w%),i 'store the 64 bits double precision HEX of the floating point to w%
c%=(w% And &H8000000000000000)>>32 'get the sign bit back to Bit32
d%=(w% And &hFFFFFE0000000)>>29 'get the shortened mantissa and move to new position
e%=(((w% And &H7FF0000000000000)>>52)+127-1023)<<23 'get the exponent and convert
o%=(c% Or d% Or e%)
Poke integer Peek(varaddr j),o% 'put the answer into the integer
else
j=0
endif
FloatToBIN32=j
endif
End Function
'---------------------------------------------------------------------------------
'This function takes an integer where the last 32 bits contain binary formatted as
'for a 32 bit float.(This could be from a device that works in single precision).
'It returns a float (single or double precision to match the current platform)
'---------------------------------------------------------------------------------
Function BIN32ToFloat(i As INTEGER) As FLOAT
Local float j
local INTEGER w%,c%,d%,e%,o%
if doubleprecision=0 then
Poke INTEGER Peek(VARADDR j),i
BIN32ToFloat=j
else
Poke word Peek(varaddr w%),i 'store the single precision HEX of the floating point
if (w% XOR &H0000000000000000) then
c%=(w% And &H80000000)<<32 'get the sign bit
d%=(w% And &h7FFFFF)<<29 'get the mantissa
e%=(((w% And &H7f800000)>>23)-127+1023)<<52 'get the exponent and convert
o%=(c% Or d% Or e%)
Poke integer Peek(varaddr j),o% 'put the answer into a double
else
j=0
endif
BIN32ToFloat=j
ENDIF
End Function
Sample code to read a single precision float from a CAN message
'This shows reading a Voltage value from Byte(7)-Byte(4) of the msg.
'and reading a current value from Byte(3)-Byte(0) of the msg.
DIM INTEGER msg 'Assume msg is the 8 bytes of data in CAN packet
... 'read msg from CAN controller
Print "Got Voltage=",BIN32ToFloat((msg AND &HFFFFFFFF00000000)>>32 )
Print "Got Current=",BIN32ToFloat((msg AND &HFFFFFFFF) )
...
Sample code to send a single precision float in a CAN message
'This shows placing a Voltage 58.5 in Byte(7)-Byte(4) of the msg.
'and a current value 19.5 Byte(3)-Byte(0) of the msg.
DIM INTEGER msg 'Assume msg is the 8 bytes of data in CAN packet
...
msg=(FloatToBIN32(58.5)<<32 AND &HFFFF000000000000) OR (FloatToBIN32(19.5) << 16 AND &HFFFF00000000)
... 'Send message to CAN conroller
print "Sent voltage=58.5 Current=19.5",hex$(id)
...
Alternate function to convert from single to double precision
A variation of the functions that accept the input as a string.
'you pass in the 4 hex bytes of the single-precision number as a string 'The function returns the double precision float function CDBL(s$) local w%,c%,d%,e%,o%,a! If Left$(s$,2)<>"&H" Then s$="&H"+s$ Poke word Peek(varaddr w%),Val(s$) 'store the single precision floating point c%=(w% And &H80000000)<<32 'get the sign bit d%=(w% And &h7FFFFF)<<29 'get the mantissa e%=(((w% And &H7f800000)>>23)-127+1023)<<52 'get the exponent and convert o%=(c% Or d% Or e%) Poke integer Peek(varaddr a!),o% 'put the answer into a double CDBL=a! end function
Alternate function to convert from double to single precision
Use this to manipulate a double precision float to extract 32 bits formatted as single precision needed to send data to a device that expects only 32bits.
'you pass in the 8 byte double-precision float number 'The function returns a hex string containing the 32bits for the single precision float. Function CSGL$(a!) Local out$="", sgl$="" sgl$= Bin2str$(single, a!, BIG) For n=1 To 4 out$=out$+Hex$(Str2bin(uint8,Mid$(sgl$,n,1)),2) Next CSGL$ = out$ End Function
Converting to and from Non-standard length signed integers
MMBasic uses 64bit signed integers. When communicating with some sensors you may need to send/receive a signed integer with less bits.
These two functions can be used for the conversion.
Ref:TBS Post@phil99
Function SSInt2SInt(Val%, Bits%) As Integer 'convert Short Signed Integer to Signed Integer SSInt2SInt = Val% - (Val% >> Bits%-1) * 2^Bits% End Function Function SInt2SSInt(Val%, Bits%) As Integer 'convert Signed Integer to Short Signed Integer SInt2SSInt = Val% And 2^Bits% -1 End Function
