View previous topic :: View next topic |
Author |
Message |
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
4 Hex bytes To Float - exponent and mantissa [SOLVED] |
Posted: Sun Nov 19, 2017 6:36 pm |
|
|
Hi all,
I have tried this:
Code: | float test=0x41200000; |
Which obviously does not work as in reality its 4 separate bytes 0x41,0x20,0x00,0x00 in a bad attempt to build a float.
i understand there is a sign, exponent and mantissa to this... but is there an easier way than bit testing and elevating each bit to the corresponding power and adding and signing?
the resulting float for this should be "10.0"
thanks,
G. _________________ CCS PCM 5.078 & CCS PCH 5.093
Last edited by Gabriel on Mon Nov 20, 2017 4:37 pm; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Nov 19, 2017 7:39 pm |
|
|
Download this zip to your Windows desktop.
http://www.piclist.com/images/floatconv10.zip
Open it, and drag floatconv.exe onto your desktop.
Run it. You can select the float format. CCS uses
"Microchip 32-bit" format for the PCM and PCH compilers.
You can enter the 4 bytes, then click the Convert to Float
button, and it will show you the decimal floating point number. |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Sun Nov 19, 2017 8:12 pm |
|
|
Hi PCM, thank you for your quick reply.
In retrospect i dont think I asked the right question so i will further explain.
I am reading a Modbus device from my PIC, the device has the data i need as a 4 hex bytes that add up to a float which is the device's sensor reading.
I want to display the sensor value to terminal as a float number that any human can understand.
Just to be clear This is not a modbus question.... its a how to convert data to float question.
is there any function to do this? how do i build the float from those bytes in code?
Edit:
Just for reference my modbus device seems to be using IEEE754 according to your app and the example provided on the original post.
Thanks.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Sun Nov 19, 2017 8:24 pm |
|
|
Hi PCM...
I'm looking at the code of that little app... seems like i can get the conversion code from the .H files...
I'll let you know if run into any problems.
Thanks! _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Sun Nov 19, 2017 10:34 pm |
|
|
For those interested in this below is my "working" code.
Works fine for numbers ending in .0 or .5 but its always a bit off for other decimals for reasons i dont yet understand.
Code: |
float Bytes_to_Float(int Byte1,int Byte2,int Byte3,int Byte4)
{
int Exponent=0;
int32 Mantissa=0;
if((Byte1+Byte2+Byte3+Byte4)==0) return(0.00); //Handle Exception
Exponent = ((Byte1 & 0x7F) << 1) + ((Byte2 & 0x80) >> 7); // Exponent is biased at this point
Exponent-=127; //Exponent is unbiased
Mantissa =(Byte2 & 0x7F);
Mantissa<<=16;
Mantissa +=(Byte3 << 8);
Mantissa +=Byte4;
fprintf(lcd_putc"Mantissa: %1.4f\r\n",((Mantissa/8388608.00)+1.00)); //Debug
if (Byte1 & 0x80) return(((pwr(2,Exponent))*((Mantissa/8388608.00)+1.00))*-1.0); //mantissa is fixed 23 bit so no point in using Pow(2,23)
else return((pwr(2,Exponent))*((Mantissa/8388608.00)+1.00));
}
|
_________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 126 Location: Bombay, India
|
|
Posted: Sun Nov 19, 2017 11:02 pm |
|
|
I would think the right way to approach this is to make a union to which you assign the individual bytes. Then read the union as a float.
union {
float f;
int8 bytes[4];
} Float_bytes;
Only, you will need to identify which byte is MSB and which is LSB and do the stuffing accordingly. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19554
|
|
Posted: Mon Nov 20, 2017 12:15 am |
|
|
Jerson's answer is right if this was a CCS float.
There is though a caveat. The data being sent is in IEEE format, not CCS. With the compiler there is a library 'ieefloat.c'. This contains the routines to convert to/from an int32 containing the bytes, to a float (and vice versa).
So:
Code: |
//your includes etc.
#include <ieeefloat.c>;
//Then retrieve the data (with the same comment about byte order).
//Use 'make32' to make the four bytes into an int32 'ifloat' say
float f_value;
f_value=f_IEEEtoPIC(ifloat);
|
This is several orders of magnitude smaller and faster than recreating the values using floating point maths (ugh...), since all it does is move the correct parts of the data to the locations required by the Microchip float format.
This actually depends on you using PCM or PCH (which the first post implies, since all the chips mentioned are PIC16/18). On PCD, the chip already works directly in the IEEE format.
The 'reason' the PIC uses a different format is historical and hardware. When the PIC first launched, the IEEE format didn't exist. Microchip designed their own 4byte float, optimising the layout to be the most efficient possible for the chip. CCS when it started, wrote their maths routines to use the same format. It saves a little time on each maths operation. When IEEE was launched, users posted here routines to convert, and a little later CCS added the library to do this. |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon Nov 20, 2017 4:37 pm |
|
|
Well this worked like a charm! Thanks Mr. T.
Now I'm left with an itch cause i don't know why my attempt was failing with the decimal precision.
Thanks
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Nov 20, 2017 10:02 pm |
|
|
Gabriel wrote: |
i don't know why my attempt was failing with the decimal precision.
|
You are shifting Byte3 left by 8, without casting it to an int16 first.
CCS doesn't automatically do this. You have to cast it.
Add the section shown in bold below:
Quote: |
Mantissa =(Byte2 & 0x7F);
Mantissa<<=16;
Mantissa +=((int16)Byte3 << 8);
Mantissa +=(Byte3 << 8);
|
|
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Tue Nov 21, 2017 5:44 am |
|
|
Huh... i thought i was shifting INTO "Mantissa" and thus the shifting byte size did not matter.
Thanks for clearing that up!
No wonder i was losing all decimal presicion!
Thank you.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19554
|
|
Posted: Tue Nov 21, 2017 6:01 am |
|
|
No.
That is a general C thing. All arithmetic sizes are based on the size of the sources not the destination. The result is put into the destination, but not till after the maths is done.
So two int8 values (say 130 and 150 in 'a' and 'b'). Destination int16 (say 'c'). Add them together as:
c=a+b;
or multiply etc., and the maths will overflow.
However if one source is larger this size will be used for the maths. So:
c=a+150L; //here the '150' is declared as a 'long'
or:
c=(int16)a+b; //convert 'a' to int16 before the arithmetic.
Will both use int16 arithmetic.
On some chips (for example the Intel processors particularly when using the hardware maths processor), the arithmetic will automatically be handled using a larger type, but this is specific behaviour to these chips, not how C defines this to be done.... |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Tue Nov 21, 2017 6:10 am |
|
|
Gabriel wrote: | Huh... i thought i was shifting INTO "Mantissa" and thus the shifting byte size did not matter. |
The definition of C is clear on this point: the type of left hand side of an assignment has no effect on the right hand side. There are two sequential processes involved in an assignment: the evaluation of the right hand expression and the setting of the left hand side, with any required type conversions/casts.
Put another way, it doesn't matter what you are "shifting into", it is what you are shifting that gets shifted, and the type of what you shift must be big enough to take whatever it is you are shifting. So, if the result of a shift is expected to need 16 bits you must cast it to a suitable (normally unsigned) type, e.g. Uint16, and then shift that.
The same applies with any arithmetic operation. If a multiply of an 8 bit integer is expected to give a sixteen bit result, then it should be cast to 16 bit first and then multiplied.
It is possible in assembler to implement more efficient operations on known smaller values, for example 8 * 8 bit multiples to get 16 bit results, 12 * 4 bit multiplies and so on. I have done may over the years. But you have to realise that C doesn't have such special cases and only implements generic 8 by 8 = 8, 16 by 16 = 16, etc. , operations. CCS C does provide some special case routines, such as _mul( ) which does optimised 8 * 8 = 16 and 16 * 16 = 32 multiplies. Also stdlib provides div() and ldiv() which provide the quotient and remainder in one division rather than two as would normally be the case. |
|
|
|