0

I am trying to create a binary package out of some custom struct (or class). I created the binary package using std::stringstream class in c++. Then, I restore it from the stream to validate the binary package. It seems fine with 'unsinged int' or 'long long' data type. But, when it comes to floating numbers ('float' or 'double'), I couldn't restore it in full precision.

Here is the simple code I've used for the test.

#include <iostream>
#include <string>

void main() {
  unsigned int idata = 1234;
  long long lldata = 123123123;
  double ddata = 343298374.123456789012345;
  float fdata = 234324.1234567;

  std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);

  // write data
  ss.write(reinterpret_cast<const char*>(&idata), sizeof(unsigned int)); // 4 bytes
  ss.write(reinterpret_cast<const char*>(&lldata), sizeof(long long)); // 8 bytes
  ss.write(reinterpret_cast<const char*>(&ddata), sizeof(double)); // 8 bytes
  ss.write(reinterpret_cast<const char*>(&fdata), sizeof(float)); // 4 bytes

  // check buffer size
  ss.seekp(0, std::ios::end);
  std::cout << "buffered: " << ss.tellp() << " bytes\n"; // expect 24 bytes

  // validate the stream
  unsigned int c_idata;
  long long c_lldata;
  double c_ddata;
  float c_fdata;
  ss.seekg(0);
  ss.read(reinterpret_cast<char*>(&c_idata), sizeof(unsigned int));
  ss.read(reinterpret_cast<char*>(&c_lldata), sizeof(long long));
  ss.read(reinterpret_cast<char*>(&c_ddata), sizeof(double));
  ss.read(reinterpret_cast<char*>(&c_fdata), sizeof(float));

  std::cout << "unsigned long: " << c_idata << std::endl;
  std::cout << "long long: " << c_lldata << std::endl;
  printf("double: %.*lf\n", 12, c_ddata);
  printf("float: %.*f\n", 12, c_fdata);  
}

I expect the binary stream size would be 24 bytes and I could restore all numbers without any loss of information. However, I couldn't restore the double and float number with full precision.

Here is the output I get when I run the above code.

buffered: 24 bytes
unsigned int: 1234
long long: 123123123
double: 343298374.123456776142
float: 234324.125000000000

Is there anything I miss or wrong?

Sungsoo Ha
  • 265
  • 4
  • 14
  • 3
    Yes, you missed printing the values before you put them in the stream. No precision was lost in the stream, you just didn't store what you thought. – Ben Voigt Apr 29 '19 at 14:50
  • 2
    You cannot serialize floating point numbers like that. It won't work across platforms/compilers. Also, whenever you use `reinterpret_cast` you need to stop and think "I'm probably doing it wrong". See also: https://isocpp.org/wiki/faq/serialization – Jesper Juhl Apr 29 '19 at 14:51
  • @JesperJuhl *You cannot serialize floating point numbers like that* Why not? – NathanOliver Apr 29 '19 at 14:52
  • 2
    @NathanOliver because when you read the number back in on a different platform you are not guaranteed to get the same exact representation - afaik. – Jesper Juhl Apr 29 '19 at 14:53
  • 3
    @JesperJuhl Sure, it not portable to send a file to systems that don't use the same floating point representation but the OP isn't storing to a file. It's in a stream so this will work on all platforms. – NathanOliver Apr 29 '19 at 14:54
  • 1
    Basically a dupe of this: https://stackoverflow.com/questions/588004/is-floating-point-math-broken – NathanOliver Apr 29 '19 at 14:55
  • @NathanOliver point. – Jesper Juhl Apr 29 '19 at 14:55
  • @FrançoisAndrieux That doesn't matter. If it was a file stream it would still work since the same system is writing and reading it. – NathanOliver Apr 29 '19 at 14:57
  • 2
    `void main` is not allowed in c++. The language requires that `main`'s return type be `int`. – François Andrieux Apr 29 '19 at 15:03
  • @JesperJuhl Thank for your comments. I will go over the link you shared. – Sungsoo Ha Apr 29 '19 at 15:10
  • @NathanOliver Thank for your comments. I will go over the post you shared. – Sungsoo Ha Apr 29 '19 at 15:12

1 Answers1

3

you lose your precision at this very moment when you declare it:

  float fdata = 234324.1234567;

not after restoration. Also, keep in mind your solution is not portable: data won't be restored correctly between the architectures with different endianness.

Dmitry
  • 1,275
  • 1
  • 5
  • 14
  • Yes, you're right. For the test purpose, I changed the number as follows: double ddata=0.123456789012345 and float fdata=0.123456789012345 expecting some loss of precision in the float number. The output was 'double: 0.1234567890123450' and 'float: 0.123456789012345', respectively. I am aware that this test code is not portable. Thanks. – Sungsoo Ha Apr 29 '19 at 15:06