0

I need to be able to read in a float or double from binary data in C++, similarly to Python's struct.unpack function. My issue is that the data I am receiving will always be big-endian. I have dealt with this for integer values as described here, but working byte by byte does not work with floating point values. I need a way to extract floating point values (both 32 bit floats and 64 bit doubles) in in C++, similar to how you would use struct.unpack(">f", num) or struct.unpack(">d", num) in Python.

here's an example of what I have tried:

stuct.unpack("d", num) ==> *(double*) str; // if str is a char* containing the data

That works fine if str is little-endian, but not if it is big-endian, as I know it will always be. The problem is that I do not know what the native endianness of the environment will be, so I need to be able to extract the binary data as big-endian at all times.

If you look at the linked question, you'll see this is easily using bitwise-ors and bitshifts for integer values, but that method does not work for floating point.

NOTE I should have pointed this out earlier, but I cannot use c++11 or any third party libraries other than Boost.

Community
  • 1
  • 1
ewok
  • 20,148
  • 51
  • 149
  • 254
  • http://stackoverflow.com/questions/5242589/would-float-point-format-be-affected-by-big-endian-and-little-endian that question was already covered – kassak Dec 14 '12 at 15:17
  • what you have tried? what is the difference? of cast? – joy Dec 14 '12 at 15:18
  • might i suggest using some sort of message packing library (MessagePack, Protobufs, Thrift, etc...) instead of trying to do this manually... This will almost quite literally make you go bald and these libraries make this sort of thing very trivial... – g19fanatic Dec 14 '12 at 15:19
  • @kassak how does that answer my question? the question wasn't "will it be affected?". obviously it is, because I am having this issue. the question was "how do I solve it?" – ewok Dec 14 '12 at 15:25
  • 1
    If the endian ness is different then you are talking between two different types of machines. Floating point format has more variables than just endian ness between machine types. – brian beuning Dec 14 '12 at 15:50
  • @brianbeuning so what does that mean? my problem is unsolvable? – ewok Dec 14 '12 at 15:52
  • ^ this. If it were the same FP encoding, a simple byteorder flip would be sufficient. If it is not simply endianness, you will have to know the encoding format used by the originating architecture and recreate the FP on the new machine – im so confused Dec 14 '12 at 15:54
  • As an addendum, while there are different encodings, you would be dealing with a very specialized format that is not IEEE compatible (IBM as an example), meaning you probably have more information about this than a generalized program would have. Should be solvable due to the limited exceptions – im so confused Dec 14 '12 at 15:59
  • Does the Python example code work? If so it's trivial to duplicate in C++. If not then you have a format problem. What machine is the source of the data? – Mark Ransom Dec 14 '12 at 17:21
  • @MarkRansom Yes, the python code works properly, assuming that I use the proper tags. to get the correct values, I need to execute `struct.unpack(">d", num)`, which specified big-endianness. – ewok Dec 14 '12 at 17:31
  • @ewok If the Python code works, then you only have endian issues. 30 years ago every machine had its own floating point format. Now almost all use the IEEE format. Since you never said the 2 machines types involved, we didn't know. – brian beuning Dec 16 '12 at 13:22

2 Answers2

0

Why working byte by byte does not work with floating point values? Just extract 32bit integer as usual, then reinterpret it as float: float f = *(float*)&i

And the same for 64bit integers and double

Simon
  • 3,224
  • 3
  • 23
  • 17
  • this works fine for 32-bit (bitshift as `long`, cast to `float`), but fails on 64-bit (`long long` -> `double`). – ewok Dec 14 '12 at 15:39
  • 1
    i'd also like to just quickly point out that in C++ you really should be using `reinterpret_cast/static_cast/dynamic_cast/const_cast` rather than C-style casts. but shouldn't make a difference here – im so confused Dec 14 '12 at 15:46
  • The C standard does not guarantee that reinterpreting objects through pointer conversions works, so casting an `int *` to `float *` and dereferencing it is not defined. Storing into a union member and reading from another union member is defined to reinterpret the bytes as the new type. You can do this with `float f = (union { unsigned int i; float f; }) {i}.f;`, provided `float` and `unsigned int` are suitable in the target platform (four bytes each, et cetera). – Eric Postpischil Dec 15 '12 at 00:43
0
void ByteSwap(void * data, int size)
{
    char * ptr = (char *) data;
    for (int i = 0;  i < size/2;  ++i)
        std::swap(ptr[i], ptr[size-1-i]);
}

bool LittleEndian()
{
    int test = 1;
    return *((char *)&test) == 1;
}

if (LittleEndian())
    ByteSwap(&my_double, sizeof(double));
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • This succeeds in switching from one-endian to other-endian, but doesn't solve the underlying problem, that my data will ALWAYS be big-endian, but I DON'T KNOW whether the machine will be. – ewok Dec 14 '12 at 17:50
  • 1
    @ewok, OK I've added that little bit for you. – Mark Ransom Dec 14 '12 at 18:02
  • @EricPostpischil, I also remember an exception for `char*`, although that was hearsay I picked up on StackOverflow. But when you think about it how else would something like `fread` work? – Mark Ransom Dec 14 '12 at 23:20
  • Oh, of course, `char` is special. Never mind. That comment belonged o the other answer, which converts `int *` to `float *`. Incidentally, `fread` is not a counterexample. It is declared to take `void *`. Additionally, the compiler is allowed to treat it specially, since it is defined in the standard. – Eric Postpischil Dec 15 '12 at 00:32