How to convert a char array to float?

Hi,

I have a problem I struggle with since hours.
Maybe you could help me with that.

I have a char array with 4 bytes filled by another function.
All four bytes repesent a 32 bit float in the reality (byte order little endian).

With the following way I try to cast the char array to float:

1
2
3
4
5
char buffer[4] = "";
//not shown, buffer is filled by an external function e.g.with the unsinged int representations buffer[0] = 240, buffer[1] = 79, buffer[2] = 3, buffer[3] = 62

//Cast to float
float value = buffer[0] |  (buffer[1] <<8) |  (buffer[2] <<16) |  (buffer[3] <<24);


What is wrong and how can it be solved?

Thanks!
What value do you expect the float to have?
Can't do the bit math in my head, but doesn't left-shifting keep the sign? Or is it simply undefined?
https://stackoverflow.com/questions/9867883/is-left-shifting-a-signed-integer-undefined-behavior-in-c03

Anyway, not sure if this will help: But make sure you're doing all the shifting on unsigned types.
So change char to unsigned char.

And as lastchance suggested, some example input & output would be nice.
Given an input of your example {240, 79, 3, 62), what do you expect the output to be?

Edit: The issue with your post is that on line 5, the result of those buffer ORs is going to be an integer. You're just assigning an integer to a float, not the actual bits.


You'll need to do pointer hackery to get what you want to work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Example program
#include <iostream>

// From: https://en.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_single-precision_binary_floating-point_format:_binary32
int main()
{
    unsigned char buffer[4];

    buffer[3] = 0b1100'0000;
    buffer[2] = 0b0000'0000;
    buffer[1] = 0b0000'0000;
    buffer[0] = 0b0000'0000;

    //Cast to float
    float value =  (*(float*)buffer); // buffer[0] |  (buffer[1] <<8) |  (buffer[2] <<16) |  (buffer[3] <<24);
    
    // output should be -2
    std::cout << value << '\n';
}


The above program prints -2, but also has a warning about strict aliasing.
 In function 'int main()':
26:30: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]


I don't know if this is defined behavior. It probably isn't technically allowed.

https://arduino.stackexchange.com/questions/4762/how-to-convert-byte-array-to-float

If you're reading from a file, perhaps istream::read or fread is more appropriate here.
Last edited on
would the "powers of two and shifting similarity" be of use here instead of hax? Not that I am above mad hax, but... ?
Thanks!

Sadly I do not know the result value.
In reality I decode a picture from a binary file.

The unsigned tipp made it!
Now everything works fine, but I am a little bit confused.
I do not know the effective bith deph of the picture.
If I read the minimum and maximum pixel intensity values from the image I get e.g. the following: Min: 1.02101e+09 Max: 1.05957e+09

If I normalize the all the values (Min = 0, Max = 255) the picture looks fine!
But I have not clue why the values are that high.

Do you have any idea?
So might this be a casting issue or is it probably the orignal data?
Last edited on
It would be really nice to know the image's real format and how you got those 2 values.
Are you SURE its not a standard RGB or RGBA or HSL type image in raw bytes and someone's funky code just manhandles the types awkwardly? Or perhaps its undoing the image's compression, which does often involve floating point types but that is as an intermediate between compressed and RGB or similar formats?

Last edited on
The original imge is a hyperspectral image stored as ENVI (uncompressed binary file + header). There I have selected one spectral channel. But all channels have these high values, which make limited sense.
These values I determined with OpenCV. So I also normalized the image.

I suspect the software which created the ENVI files made some unknown preprocessing.
In that case yea, who knows what the expected values are going to be... maybe you have the right answer and just can't verify it easily? Do you have a baseline image with known data you can check against?

I did a very small amount of HS imagery long ago. I don't remember much of it, I just helped someone else a little... seems like we mixed and matched them into 24 bit images until we saw something interesting … was from something nasa took 15 years or so back...
Thank you all for the help!

I now got a input / output combination.

In the char arrary I have the binary representations of the unsigned integers as the following: buffer[0] = 222, buffer[1] = 216, buffer[2] = 247, buffer[3] = 60.
This converts wrong.

1
2
3
4
5
6
7
8
9
char buffer[4] = "";
//now filles with the data from above.
    unsigned char *uBuf32bit = (unsigned char *)buffer;
    float value_32=  uBuf32bit[0] |  (uBuf32bit[1] <<8) |  (uBuf32bit[2] <<16) |  (uBuf32bit[3] <<24);

//outcome: value = 1.02288e+09

//correct result would be: value= 0.0302548


Any ideas?
Last edited on
You need to get the bits into a float, but not through assignment.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main ( ) {
    unsigned char buffer[4];
    buffer[0] = 222;
    buffer[1] = 216;
    buffer[2] = 247;
    buffer[3] = 60;
    union {
        float f;
        int i;
    } f;
    f.i = buffer[0] |  (buffer[1] <<8) |  (buffer[2] <<16) |  (buffer[3] <<24);
    printf("Float=%f\n", f.f);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

union Convert
{
   unsigned char byte[4];
   float real;
};

int main()
{
   Convert convert;

   convert.byte[0] = 222;
   convert.byte[1] = 216;
   convert.byte[2] = 247;
   convert.byte[3] = 60;

   cout << "Float is " << convert.real << '\n';
}


Float is 0.0302548



The following also gives the same answer (on my PC).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
   {
      ofstream strm( "temp" );
      for ( int i : { 222, 216, 247, 60 } ) strm << (unsigned char)i;
   }
   {
      float f;
      ifstream strm( "temp", ios::binary );
      strm.read( (char *)(&f), 4 );
      cout << "Float is " << f << '\n';
   }
}

Last edited on
Thank you very much!!!

This works really nice :-)
Topic archived. No new replies allowed.