4

I am writing a double value in Java into a file like below,

byte[] bytes = new byte[8];
//Double d = new Double(4);
double d =1000;
ByteBuffer.wrap(bytes).putDouble(d);
File test = new File(FILE_PATH+"readme.bin");
test.createNewFile();
FileOutputStream ostream = new FileOutputStream(test);
ostream.write(bytes);
ostream.close();

and I can read it back in with Java code, but when retrieving it in C code I get the value as 0.000000. My C code is as follows,

FILE *file_ptr;
file_ptr = fopen(file_absolute,"rb");
if (!file_ptr)
{
    printf("Unable to open file!");
    return 1;
}
char bytes[8];
fread(&bytes, 8, 1, file_ptr);

double d = *((double*)bytes);
printf("%f",d);

Am I missing anything here? Both C and Java code are running on the same system.

Jongware
  • 22,200
  • 8
  • 54
  • 100
user2206366
  • 461
  • 3
  • 6
  • 17
  • 2
    I'm sure you could write some code to convert the bytes of a Java `double` into a C `double` (the formats are different, which is why your code isn't working) but I'd suggest that it is a bad idea if you have any choice in the matter. To transfer data between languages it would be much safer (and easier to debug) if you used a text representation of the `double` value. See, for example [The Importance of Being Textual](http://www.catb.org/esr/writings/taoup/html/ch05s01.html) – Simon May 08 '16 at 02:48
  • 1
    @Simon, whereas I agree that a textual representation would be safer, I fail to see how you can confidently claim that the formats of Java and C `double`s differ. It is possible that they do differ on the OP's machine, but unlikely, as Java definitely uses IEEE 754 binary double precision format, and so does any C runtime you're likely to meet (though C itself does not specify). – John Bollinger May 08 '16 at 04:17
  • 1
    I don't see an obvious error in either code, but if you cannot change to a text format then I suggest you start your debugging by verifying that the data you read were the same data you think you wrote, and that those are in fact the data that you *meant* to write. I'm inclined to guess that the problem is somewhere there. – John Bollinger May 08 '16 at 04:23

2 Answers2

0

The Java code outputs the bytes in the reverse order from what C expects.

So you need to reverse the byte order. The following will work in standard little-endian operating systems:

#include <stdio.h>

void main() {
  FILE *file_ptr;
  unsigned char bytes[sizeof(double)];
  union {
    double d;
    unsigned char bytes[sizeof(double)];
  } u;

  int c;

  file_ptr = fopen("float.bin","rb");
  fread(bytes,sizeof(double),1,file_ptr);

  for (c=0; c<sizeof(double); c++)
    u.bytes[c]=bytes[sizeof(double)-1-c];

  printf("%f\n", u.d);
}

This will work whenever the target system is little endian for any standard system and C compiler I've seen.

If you want to make the code more portable you can detect the endianness of the C code at compile time (using macros, see https://sourceforge.net/p/predef/wiki/Endianness/), or at runtime. There are some not too uncommon systems, especially ARM-, and MIPS-based, which are often big-endian, and here the conversion is not required. Java will always produce big endian output, regardless of the underlying system, so only the C code needs to be conditional.

For a very portable version you could detect the byte order with macros, or at runtime, both of which options are explained at the link given about. If you are only ever going to run this code on Windows, or PC's running Linux, and don't care about portability, you can just use the code above.

ThomasH
  • 1,085
  • 10
  • 12
  • 1
    The Java code *may* output the bytes in the reverse order. Whether that's true depends on the hardware, the C compiler, ... You don't have any idea whether 'the following will work' unless you're executing on the same hardware and compiling with the same C compiler as the OP, and he hasn't specified either. You also don't have any idea whether this solves the problem, for the same reasons. – user207421 May 08 '16 at 09:50
  • Java will always output it in big endian, and every C compiler I've seen will use little-endian on a little-endian system. If you're aiming for some truly portable code it's just a matter of testing endianness at compile time and using a macro to make the reversing code conditional, like here: https://sourceforge.net/p/predef/wiki/Endianness/ – ThomasH May 08 '16 at 16:49
  • `double d = *((double*)bytes2);`? That's undefined behavior and a `SIGBUS` waiting to happen on any hardware with alignment restrictions. – Andrew Henle May 08 '16 at 23:21
  • @AndrewHenle You're of course right, I was just copying the same pattern from the original question. Will update answer to address that. – ThomasH May 08 '16 at 23:47
-1
byte[] bytes = new byte[8];
//Double d = new Double(4);
double d =1000;
ByteBuffer.wrap(bytes).putDouble(d);
File test = new File(FILE_PATH+"readme.bin");
test.createNewFile();
FileOutputStream ostream = new FileOutputStream(test);
ostream.write(bytes);
ostream.close();

You don't need all this. You can do the same in less than half the code, with:

DataOutputStream dos = new DataOutputStream(new FileOutputStream(test));
dos.writeDouble(1000);
dos.close();

Specifically, the File.createNewFile() call is a pointless waste of time and space, and all the mucking around with ByteBuffers and byte arrays is done much more simply via DataOutputStream.

However the problem you have is that your hardware's C double clearly isn't in the same format as your Java double was written to the file. Note that, contrary to some comments under your question, this is indeed possible, as C isn't obliged to respect IEEE-754's bit format or byte order.

See here for more information.

Community
  • 1
  • 1
user207421
  • 305,947
  • 44
  • 307
  • 483