Pages: [1]   Go Down
Author Topic: Convert hex string to float - how?  (Read 1998 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all -

My Arduino board will receive via Serial.read() a four-character hexadecimal string that I would like to convert into a float. The four character string is the IEEE754 representation of the float using four bytes.

I've looked all over the interwebs to figure out how to do this elegantly and I can't find anything - just how to convert a float to a string.

Can anyone help me? Thanks!
« Last Edit: March 31, 2011, 01:28:10 pm by strictlyrude27 » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 290
Posts: 25770
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Does the hex represent an IEEE754 float value?
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Does the hex represent an IEEE754 float value?

Yessir. OP edited to denote that.
Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 108
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

//Converting a float to 4 bytes:
float float_var;
byte byte_array[4];

memcpy(byte_array,&float_var,4);

// Use magic to transport the 4 bytes to the other computer. Put the bytes in an identical array as the sending computer.

//Copy them back!
float float_var;
byte byte_array[4];

memcpy(&float_var,byte_array,4);

Everything is made of bytes. There is nothing stopping you from taking them apart and putting them back together through pointer and memory manipulation.

With a float, byte ordering is the same in all systems. But with 16 and 32 bit ints, Intel PCs and Atmel microcontrollers store the bytes in a different order. This is called "Byte Ordering". You have to swap the bytes around if you use this method to send ints or longs.
Logged

Linux and Arduino, two great things that go great together!
http://www.roboticcore.com

Left Coast, USA
Offline Offline
Sr. Member
****
Karma: 5
Posts: 499
Sometimes I just can't help myself.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

//Converting a float to 4 bytes:
.
.
.
Everything is made of bytes. There is nothing stopping you from taking them apart and putting them back together through pointer and memory manipulation.

I'm with you up to here.

However...

Quote from: TeslaFan
With a float, byte ordering is the same in all systems.
Not true.  I have an application running on an Intel IXP420 arm-xscale processor.  Integer variables and floating point variables are all stored big-endian.  On my i386 workstations and on my ATmega boards, they are all stored little-endian.  See Footnote.

Quote from: TeslaFan
But with 16 and 32 bit ints, Intel PCs and Atmel microcontrollers store the bytes in a different order.
No they are not.  With avr-gcc (Arduino's compiler), integer and floating point variables are all stored little-endian (same as on my i386 Windows and Linux workstations).

Quote from: TeslaFan
You have to swap the bytes around if you use this method to send ints or longs.
You have to swap bytes if (and only if) the transmitter and receiver are different-endian.  Period. Full Stop.

Higher level network protocols usually (almost always, I think) specify that binary bytes (octets) of multi-byte numerical quantities be sent in big-endian order.  Compiler vendors usually supply byte-swapping functions or macros that convert machine-endian integers to network-endian (i.e. big-endian) representation.    By using vendor-supplied functions like htons, htonl, ntohs, ntohl, etc. the program source can be compiled on either type of system without modification.  These create no overhead for big-endian systems but actually require that the processor do some work little-endian systems.

Regards,

Dave

Footnote:

Arduino sketch:
Code:
void setup()
{
    Serial.begin(9600);
    uint16_t x = 0x1234;
    float z = 1.125e4;
    unsigned i;
    unsigned char *chpt;
    Serial.print("Here's the 16-bit integer         : 0x");
    Serial.println(x, HEX);
    chpt = (unsigned char *)&x;
    Serial.print("Here are the bytes in memory order: ");
    for (i = 0; i < sizeof(x); i++) {
        Serial.print("0x");
        Serial.print(chpt[i],HEX);
        Serial.print(" ");
    }
    Serial.println();Serial.println();
    chpt = (unsigned char *)&z;
    Serial.print("Here's the float                  : ");Serial.println(z);
    Serial.print("Here are the bytes in memory order: ");
    for (i = 0; i < sizeof(z); i++) {
        Serial.print("0x");
        Serial.print(chpt[i],HEX);
        Serial.print(" ");
    }
    Serial.println();
}
void loop(){}

Output from Duemilanove:


Here's the 16-bit integer         : 0x1234
Here are the bytes in memory order: 0x34 0x12

Here's the float                  : 11250.00
Here are the bytes in memory order: 0x0 0xC8 0x2F 0x46


The integer is obviously stored least-significant byte first, right?
Then...
Look at the bytes of the floating point number and you can see that the float is stored least-significant byte first.
(You can work it out using a reference like: Floating Point on Wikipedia.)


Equivalent standard C program compiled with GNU gcc on my Linux workstation:
Code:
#include <stdio.h>
#include <stdint.h>

int main()
{
    uint16_t x = 0x1234;
    float z = 1.125e4;
    unsigned i;
    unsigned char *chpt;

    chpt = (unsigned char *)&x;
    printf("Here's the 16-bit integer         : 0x%02x\n", x);
    printf("Here are the bytes in memory order: ");
    for (i = 0; i < sizeof(x); i++) {
        printf("0x%02x ", chpt[i]);
    }
    printf("\n");
    printf("Here's the float                  : %.2f\n", z);
    chpt = (unsigned char *)&z;
    printf("Here are the bites in memory order: ");
    for (i = 0; i < sizeof(z); i++) {
        printf("0x%02x ", chpt[i]);
    }
    printf("\n");
    return 0;
}

Output:

Here's the 16-bit integer         : 0x1234
Here are the bytes in memory order: 0x34 0x12
Here's the float                  : 11250.00
Here are the bites in memory order: 0x00 0xc8 0x2f 0x46


Same results as with Arduino: Little-endian all the way.

Then:

When I cross-compiled the same C program with arm-linux-gcc for the xscale and ran the result on the target system, here's the output:

Here's the 16-bit integer         : 0x1234
Here are the bytes in memory order: 0x12 0x34
Here's the float                  : 11250.00
Here are the bites in memory order: 0x46 0x2f 0xc8 0x00


Big-endian all the way!

Final note (I promise.  Really)
I don't remember ever having to send binary floating point numbers from workstation to arm-xscale system (or anything else to anything else either), but if I ever need to, I might check this out: Floating-point and Endianness on Wikipedia
« Last Edit: March 31, 2011, 04:33:01 pm by davekw7x » Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 108
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Is that right? I could have sworn I was sending data from Linux unswapped and swapping it on receipt on the Arduino end.

Heh. Dave, you're absolutely right. I was swapping on both sides. I had deluded myself into thinking the Arduino was ordered the other way.

Thanks for correcting that!
Logged

Linux and Arduino, two great things that go great together!
http://www.roboticcore.com

Ontario
Online Online
God Member
*****
Karma: 24
Posts: 860
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Code:
//Converting a float to 4 bytes:
float float_var;
byte byte_array[4];

memcpy(byte_array,&float_var,4);

Save yourself some memory and copying...

Code:
union {
   //Converting a float to 4 bytes:
   float float_var;
   byte byte_array[4];
} both;

  both.float_var = 3.1415926535;

  send(both.byte_array[0]);  // or whatever it is you want to do with the bytes
  send(both.byte_array[1]);
  send(both.byte_array[2]);
  send(both.byte_array[3]);
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Code:
//Converting a float to 4 bytes:
float float_var;
byte byte_array[4];

memcpy(byte_array,&float_var,4);

Save yourself some memory and copying...

Code:
union {
   //Converting a float to 4 bytes:
   float float_var;
   byte byte_array[4];
} both;

  both.float_var = 3.1415926535;

  send(both.byte_array[0]);  // or whatever it is you want to do with the bytes
  send(both.byte_array[1]);
  send(both.byte_array[2]);
  send(both.byte_array[3]);

This worked fine, although I ran into the endianness problem described above. I was receiving a hex string big-endian style and needed to flip to little-endian before the float was represented correctly.

Thanks to everyone for their help and insight on this!
Logged

Pages: [1]   Go Up
Jump to: