4

I have a binary file, and I want to read a double from it.

In hex representation, I have these 8 bytes in a file (and then some more after that):

40 28 25 c8 9b 77 27 c9 40 28 98 8a 8b 80 2b d5 40 ...

This should correspond to a double value of around 10 (based on what that entry means).

I have used

#include<stdio.h>
#include<assert.h>

int main(int argc, char ** argv) {
   FILE * f = fopen(argv[1], "rb");
   assert(f != NULL);
   double a;
   fread(&a, sizeof(a), 1, f);
   printf("value: %f\n", a);
}

However, that prints value: -261668255698743527401808385063734961309220864.000000

So clearly, the bytes are not converted into a double correctly. What is going on? Using ftell, I could confirm that 8 bytes are being read.

4
  • 3
    Where is the code that wrote these "bytes" , and was it from the same platform? Commented Sep 19, 2014 at 7:59
  • It looks like the original system wrote data out as big endian and you're trying to read it back as little endian - you just need to reverse the order of the 8 bytes. Commented Sep 19, 2014 at 8:05
  • 1
    Btw, 12.0738for the first, 12.2979 for the second, etc if you reverse the bytes, so... reverse the bytes if reading on an LE platform. I sincerely hope if you do that all platforms write the data in BE. Commented Sep 19, 2014 at 8:07
  • Great! I was able to use fgetc with the union trick from stackoverflow.com/questions/4949144/how-to-byteswap-a-double Commented Sep 19, 2014 at 8:15

2 Answers 2

4

Just like integer types, floating point types are subject to platform endianness. When I run this program on a little-endian machine:

#include <stdio.h>
#include <stdint.h>

uint64_t byteswap64(uint64_t input) 
{
    uint64_t output = (uint64_t) input;
    output = (output & 0x00000000FFFFFFFF) << 32 | (output & 0xFFFFFFFF00000000) >> 32;
    output = (output & 0x0000FFFF0000FFFF) << 16 | (output & 0xFFFF0000FFFF0000) >> 16;
    output = (output & 0x00FF00FF00FF00FF) << 8  | (output & 0xFF00FF00FF00FF00) >> 8;
    return output;
}

int main() 
{
    uint64_t bytes = 0x402825c89b7727c9;
    double a = *(double*)&bytes;
    printf("%f\n", a);

    bytes = byteswap64(bytes);
    a = *(double*)&bytes;
    printf("%f\n", a);

    return 0;
}

Then the output is

12.073796
-261668255698743530000000000000000000000000000.000000

This shows that your data is stored in the file in little endian format, but your platform is big endian. So, you need to perform a byte swap after reading the value. The code above shows how to do that.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the detailed answer. In the end I used fgetc with the union trick from stackoverflow.com/questions/4949144/how-to-byteswap-a-double
8 years later I end up here. One sidenote: this loses precision: 36.24139221666073 -> 36.241392 I'm still looking for the "fix" to this.
@soze no, it doesn't lose precision, you have misdiagnosed
2

Endianness is convention. Reader and writer should agree on what endianness to use and stick to it.

You should read your number as int64, convert endianness and then cast to double.

2 Comments

Casting won't work, when you want to re-interpret the same bits. Casting will convert the (very large) integer to a very large double.
Pointer-casting or union-casting would work; the latter being safer in case of size mismatch, since at least you know it won't overflow on nearby variables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.