How can I convert a 32Bit Floating Point Hex Value (eg. 3FBD70A4) to a floating point representation in decimal (≈1.48) in CRBasic?
I have tried using the HextoDec instruction,
however the result is an NAN.
If it can't be done with a built-in instruction, the long handed way to do it is to convert the hex to binary, extract the sign, exponent & mantissa and then perform the maths. Perhaps someone has previously done this?
The hex argument must be a "string" variable, so try putting double-quotes around it like this:
When the Hex string is fixed (known in advance) you can also use this syntax:
However, I now understand your problem a bit better. The two syntaxes above assume that the hex characters represent an integer, and in your case they represent a floating point value. So you will have to use "MoveBytes" to get this to work. (You will stuff 1 byte at a time into the Result variable). I'll work on this and get back to you.
Here is how you use MoveBytes to do this:
&H3F creates a Long int with the value of 3F (63) in its last/fourth byte (byte #3 of 0,1,2,3)
That is what the "&H3F,3" arguments mean.
The first MoveBytes instruction moves the value 3F into the first byte of FloatResult (#0 of 0,1,2,3)
The second instruction moves &HBD into the 2nd byte (#1)
3rd instruction moves &H70 int 3rd byte (#2)
4th instruction moves &HA4 into 4th byte (#3).
My test program is given here (it calculates 1.48 for FloatResult):
Public HexString As String
HexString = "7F"
Result2 = HexToDec(HexString)
Result3 = &H3FBD70A4
Note that the variables Result and Result3 do not give 1.48 for the value since they assume the total hex string to be sourced as an integer not sourced as an IEEE4 Float.
After the program runs you can change the value of HexString and see its decimal conversion in Result2
I thought about this some more. You don't have to move the bytes one at a time.
You can do it with one instruction like this:
I tested it and it correctly gave 1.48.
Note also that depending on what kind of source CPU generates the bytes, that they might need to be reversed (Motorola vs. Intel - Big endian vs. Little endian). However that reversal doesn't seem to be necessary in your case.
Thanks GTProdMgr, I tried the MoveBytes(FloatResult,0,&H3FBD70A4,0,4) code and it did also give me 1.48, which is perfect.
However the Hex value I gave (3FBD70A4) was an example, as the real hex value is a variable read digitally through a com port.
I'm having trouble getting the MoveBytes instruction to operate correctly when using a string containing the raw hex value. Here is a snipped of the code I'm using:
Public ResponseString As String *30
Public Result As Float
Scan (1,Sec,0,0) SerialInRecord(Com1,ResponseString,0,4,&H101F,ResponseLength,01) MoveBytes(Result,0,&H+ResponseString,0,4) NextScan
If in the above code the sensor fills ResponseString with a value of 43 52 00 00 in hex (ignore the spaces), then the result should be 2.1E2, however the result on the logger is 6.95E-16.
Is there a trick to getting the MoveBytes to work with a variable?
Remove the &H+ from within MoveBytes.
Thank you for your contribution but unfortunately removing &H+ did not fix the problem.
I have tried the following formats for the SourceMoveBytes parameter...
If ResponseString equals hex 43 87 00 00 and SourceMoveBytes is: &H+ResponseString
the logger result is 4.852276e-041, however the result should be 2.7e2.
If ResponseString equals hex 43 7A 00 00 and SourceMoveBytes is: "&H"+ResponseString the logger result is 6.938928e-016, however the result should be 2.5e2.
If ResponseString equals hex 43 82 00 00 and SourceMoveBytes is: ResponseString the logger result is 4.665893e-010, however the result should be 2.6e2.
I suspect the logger is somehow treating ResponseString as ASCII and not Hexadecimal, either when it stores the hex values in ResponseString (which is a String variable) or the format of SourceMoveBytes is incorrect?
I've been looking at this. The problem is that MoveBytes needs a Source variable or constant that is of type Long or Float, and not a String. You have been passing it in as a string.
HexToDec will convert your Hex string to a Long [4 bytes] (no need for "&H - that only applies when you know the hex ahead of time).
This gave me the expected value of 210:
When HexString had the value of "43520000".
Here is my Program:
Public HexString As String
Public IntResult As Long
HexString = "3FBD70A4"
IntResult = HexToDec(HexString)
After the program runs, change the value of HexString from "3FBD70A4" to "43520000" and then FloatResult1 will be recalculated. You should be able to try out any hex value with this program by modifying HexString at runtime.
It is a bit subtle, but when you see something like &H3FBD70A4 in a CRBasic program, it gets interpreted as a Long integer and not a string. That's why an expression like that works inside of the MoveBytes instruction. In your case the basic input (via SerialInRecord) was a string, so it needed to be converted over to Long to be usable by MoveBytes.
This approach should work smoothly in your program:
Only use the "&H" notation when using a *constant* integer ( known at the time you write the program) and which will be spelled out in hex notation.
Thank you so much for your help GTProdMgr.
I was finally able to read the sensors hexadecimal output by changing the ResponseString variable (that is the destination variable for the SerialInRecord instruction) from a string to a long as you suggested.
I had also overlooked the reverse byte order of the ResponseString. The final code I used was:
Public ResponseString As Long Public ResponseLength Public Result As Float BeginProg SerialOpen(Com1,38400,19,0,50) Scan (1,Sec,0,0) SerialOut(Com1,CHR(16)+CHR(19)+CHR(06)+CHR(16)+CHR(31)+CHR(155)+CHR(191),"",0,100) SerialInRecord(Com1,ResponseString,0,4,&H101F,ResponseLength,01) 'Convert Reverse Order Hexadecimal 32bit folating-point value from sensor to "IEEE4 Float" MoveBytes(Result,0,ResponseString,3,1) MoveBytes(Result,1,ResponseString,2,1) MoveBytes(Result,2,ResponseString,1,1) MoveBytes(Result,3,ResponseString,0,1) NextScan EndProg
So in the above code if the sensor outputs 00 00 7A 43 into ResponseString the final Result will equal 250.
Your explanation on the MoveBytes instruction really helped me out.
I'm happy to be of help, and happy that it is working for you.
I'm glad I mentioned the "need for byte-reversal in some cases" since you did need it in the end. With so many electronic devices out in the world that can transmit data, you never know what kind of CPU your device may be using ("Big-endian/MSB first" vs. "Little-endian/LSB first").
I just wanted to add a couple of related comments for future reference:
Typically, SerialInRecord and other "SerialIn" instructions use the"String" variable type as the "Dest" variable for holding the incoming data.This is because the input is often longer than 4 bytes (whether the input is ASCII/ANSI/Unicode strings or whether it is binary)
In your case the fact that you were bringing in exactly 4 bytes made it possible to put the incoming data into a Long integer variable directly (thus bypassing the need for the HexToDec instruction).
CRBasic doesn't have a "byte" data type for in-memory variables. In-memory variables are only of type Float/IEEE4, Long, Boolean, or String. Also - Float, Long, and Boolean are all exactly 4 bytes in size. For situations in which "byte streams" need to be handled in groupings greater than 4 (i.e., long binary sequences), these are typically handled as "strings" - in such cases the String variables are used like "byte arrays".
Precision is the main difference where double is a double precision (64 bit) floating point data type and decimal is a 128-bit floating point data type.
Double - 64 bit (15-16 digits)
Decimal - 128 bit (28-29 significant digits)
So Decimals have much higher precision and are usually used within monetary (financial) applications that require a high degree of accuracy. But in performance wise Decimals are slower than double and float types. Double Types are probably the most normally used data type for real values, except handling money. More about......Decimal vs Double vs Float