0

I am parsing a header in a file. The file has some float value it wants me to extract. The comments say its a float32 value but I'm not sure how to extract it.

Given a buffer like this, how do I properly extract this? Note it is little endian.

unsigned char b[4];
b[0] = 0xa1;
b[1] = 0xb2;
b[2] = 0xc3;
b[3] = 0xd4;

float f = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
printf("f is %f\n", f);

To be more clear: the problem is that no matter what the value in the file (buffer) the decimal is always .000000. How do I know where that decimal should go?

  • Something like this? https://ideone.com/8SwJ9x – pmg Jul 14 '20 at 20:16
  • Try `printf("%g\n", (union { unsigned char b[4]; float f;}){ {0xa1, 0xb2, 0xc3, 0xd4} }.f);` – chux - Reinstate Monica Jul 14 '20 at 20:18
  • @pmg I should have cleared up: the value shouldnt be something like `-6724124147712.000000`, but more along the lines of 124.49.. Ie a float with decimal values. I just don't get where in the binary/hex representation the decimals start. –  Jul 14 '20 at 20:58
  • See [wikipedia article](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) – pmg Jul 14 '20 at 21:00
  • @pmg I literally don't feel smart enough to figure this out. :*( –  Jul 14 '20 at 21:08

2 Answers2

3

The safe way:

unsigned char b[sizeof(float)];
float f;
memcpy(&f, b, sizeof(f));

it is very likely that good compiler will optimize out the call to the memcpy and this method will be very efficient.

gcc safe way

union {
  unsigned char b[sizeof(float)];
  float f;
}a = {.b = {0xa1, 0xb2, 0xc3, 0xc4}};

printf("%f" , a.f); 

or pointer punning as in the @AnttiHaapala answer

0___________
  • 60,014
  • 4
  • 34
  • 74
2

If the target architecture has float32 as the representation, and it is of same endianness as the source, then as simple as:

float f;
unsigned char *b = (unsigned char *)&f;

b[0] = 0xa1;
b[1] = 0xb2;
b[2] = 0xc3;
b[3] = 0xd4;

printf("f is %f\n", f);

If the data source and your target architecture have little/big endianness mismatch, just reverse the indices to 3 ... 0. If you have some other weird legacy endianness, I pity you.


Note that you can use memcpy - for which you would need to use an auxiliary buffer and not just something that iterates bytes; or an union which would be more code, and is not necessarily C++-compatible.

  • 2
    Since C99, I suspect this runs afoul of the [strict aliasing rule](https://stackoverflow.com/a/30875760/2410359). – chux - Reinstate Monica Jul 14 '20 at 20:28
  • 2
    @chux, the strict-aliasing rule explicitly allows accessing any object (or part of one) via an lvalue of character type. – John Bollinger Jul 14 '20 at 20:35
  • [C11 Standard - §6.5 Expressions (p6,7)](http://port70.net/~nsz/c/c11/n1570.html#6.5p6) (last bullet) `"— a character type"` – David C. Rankin Jul 14 '20 at 20:36
  • Yes, but code here is not reading the aliased data with a character pointer, but a `float `. The other way around is OK. Doe not work both ways and this looks like the wrong one. – chux - Reinstate Monica Jul 14 '20 at 20:38
  • No, @chux. The `float` object designated by `f` is being accessed via the lvalues `b[0]`, *etc*., which have type `unsigned char`. "Access" is inclusive of both reads and writes in C Standardese. – John Bollinger Jul 14 '20 at 20:41
  • It is a warning on `"-Wincompatible-pointer-types"` assigning the address of `float` to `unsigned char*`, but not a strict alias violation. (thus the comment from @EricPostpischil) – David C. Rankin Jul 14 '20 at 20:42
  • Yes. The assignment requires a cast, as Eric already pointed out. GCC provides an extension by accepting the assignment without it, but that does not factor into the strict-aliasing analysis. – John Bollinger Jul 14 '20 at 20:45
  • @JohnBollinger To be clear you are asserting because `b` is a character pointer, with the assignment `b[0] = 0xa1;`, the compiler is obliged to note that this may change `f` and not treat `printf("f is %f\n", f);` as printing an uninitialized object? – chux - Reinstate Monica Jul 14 '20 at 20:58
  • @chux-ReinstateMonica, I am asserting that because `b` is a character pointer *that points to the first character of the object designated by `f`*, the compiler is obliged to recognize that assignments to `b[0]` ... `b[sizeof(f) - 1]` **do** modify `f`. This does not preclude the compiler recognizing or assuming that assignments through various other `char` pointers are without any effect on `f`. – John Bollinger Jul 14 '20 at 21:10
  • Slowly but surely I'll digest 2018 `:)` – David C. Rankin Jul 14 '20 at 21:14
  • @JohnBollinger Thanks. – chux - Reinstate Monica Jul 14 '20 at 21:20
  • @EricPostpischil oops, fixed, this was the last thing I did before going to bed, and it shows... – Antti Haapala -- Слава Україні Jul 15 '20 at 03:54