How to implement serialization and de-serialization of a double? - c++

I am trying to solve the relatively simple problem of being able to write a double to a file and then to read the file into a double again. Based on this answer I decided to use the human readable format.
I have successfully circumvented the problems some compilers have with nan and [-]infinity according to this question. With finite numbers I use the std::stod function to convert the string representation of a number into the number itself. But from time to time the parsing fails with numbers close to zero, such as in the following example:
#include <cmath>
#include <iostream>
#include <sstream>
#include <limits>
const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);
int main()
{
std::stringstream stream;
stream.precision(maxPrecision);
stream << small;
std::cout << "serialized: " << stream.str() << std::endl;
double out = std::stod(stream.str());
std::cout << "de-serialized: " << out << std::endl;
return 0;
}
On my machine the result is:
serialized: 9.2263152681638151025201733115952403273156653201666065e-318
terminate called after throwing an instance of 'std::out_of_range'
what(): stod
The program has unexpectedly finished.
That is, the number is too close to zero to be properly parsed. At first I thougth that the problem is that this number is denormal, but this doesn't seem to be the case, since the mantissa starts with a 9 and not a 0.
Qt on the other hand has no problems with this number:
#include <cmath>
#include <limits>
#include <QString>
#include <QTextStream>
const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);
int main()
{
QString string = QString::number(small, 'g', maxPrecision);
QTextStream stream(stdout);
stream.setRealNumberPrecision(maxPrecision);
stream << "serialized: " << string << '\n';
bool ok;
double out = string.toDouble(&ok);
stream << "de-serialized: " << out << '\n' << (ok?"ok":"not ok") << '\n';
return 0;
}
Outputs:
serialized: 9.2263152681638151025201733115952403273156653201666065e-318
de-serialized: 9.2263152681638151025201733115952403273156653201666065e-318
ok
Summary:
Is this a bug in the gcc implementation of standard library?
Can I circumvent this elegantly?
Should I just use Qt?

Answering question #2:
This is probably my "C-way" kind of thinking, but you could copy the double into a uint64_t (mem-copying, not type-casting), serialize the uint64_t instead, and do the opposite on de-serialization.
Here is an example (without even having to copy from double into uint64_t and vice-versa):
uint64_t* pi = (uint64_t*)&small;
stringstream stream;
stream.precision(maxPrecision);
stream << *pi;
cout << "serialized: " << stream.str() << endl;
uint64_t out = stoull(stream.str());
double* pf = (double*)&out;
cout << "de-serialized: " << *pf << endl;
Please note that in order to avoid breaking strict-aliasing rule, you actually do need to copy it first, because the standard does not impose the allocation of double and uint64_t to the same address-alignment:
uint64_t ismall;
memcpy((void*)&ismall,(void*)&small,sizeof(small));
stringstream stream;
stream.precision(maxPrecision);
stream << ismall;
cout << "serialized: " << stream.str() << endl;
ismall = stoull(stream.str());
double fsmall;
memcpy((void*)&fsmall,(void*)&ismall,sizeof(small));
cout << "de-serialized: " << fsmall << endl;

If you're open to other recording methods you can use frexp:
#include <cmath>
#include <iostream>
#include <sstream>
#include <limits>
const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);
int main()
{
std::stringstream stream;
stream.precision(maxPrecision);
int exp;
double x = frexp(small, &exp);
//std::cout << x << " * 2 ^ " << exp << std::endl;
stream << x << " * 2 ^ " << exp;
int outexp;
double outx;
stream.seekg(0);
stream >> outx;
stream.ignore(7); // >> " * 2 ^ "
stream >> outexp;
//std::cout << outx << " * 2 ^ " << outexp << std::endl;
std::cout << small << std::endl << outx * pow(2, outexp) << std::endl;
return 0;
}

Related

Why can't I use fixed and setprecision() with +operator for strings instead of <<operator for cout

Why can't I use "fixed" and "setprecision()" with the +operator to format it into a string and that I only can use it only with the less-than-less-than-operator to format it for cout. What are other ways I can implement this?
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double a = 157.2734;
cout << "This number is " << fixed << setprecision(1) << a << "." << endl;
string line = "This number is " + fixed + setprecision(1) + a + "." + "\n"; // This doesn't work this way! Why!?!?!?
cout << line;
return 0;
}
Why can't I use "fixed" and "setprecision()" with the +operator to format it into a string?
Have a closer look at std::fixed and std::setprecision().
The complete signature of std::fixed:
std::ios_base& fixed(std::ios_base& str);
So, it is designed to work with streams exclusively.
In the case of std::setprecision(), it's a bit tricky:
/*unspecified*/ setprecision( int n );
However:
Returns an object of unspecified type such that if str is the name of an output stream of type std::basic_ostream or an input stream of type std::basic_istream, then the expression str << setprecision(n) or str >> setprecision(n) behaves as if the following code was executed:
str.precision(n);
So, it might have worked if there were a std::string::precision() method but there isn't one.
What are other ways I can implement this?
The possible solution:
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
int main()
{
double a = 157.2734;
cout << "This number is " << fixed << setprecision(1) << a << "." << endl;
ostringstream fmtStr;
fmtStr << "This number is " << fixed << setprecision(1) << a << ".\n";
string line = fmtStr.str();
cout << line;
return 0;
}
Output:
This number is 157.3.
This number is 157.3.
Life demo on ideone

Configuring std::ofstream format for floating point numbers

Is there a way to configure ostream using iomanip to output floating point numbers as follows:
0.00000000000000E+0000
3.99147034531211E-0003
...
I am translating code from pascal to C++ and I need to output numbers in exactly same format. It is preferable to use std::ofstream instead of fprintf or other C library functions.
One way to do this is with some string manipulation. Format to a stringstream using scientific notation, then split the string on the 'e'. Now you have the parts you can format yourself.
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
std::string format(double val)
{
std::ostringstream oss;
oss << std::scientific << std::setprecision(14) << val;
auto result = oss.str();
auto match = result.find('e');
if (match == std::string::npos)
{
// Should never get here -- maybe throw
}
oss.str("");
auto exp = std::stoi(result.substr(match+1));
oss << result.substr(0, match) << 'E'
<< std::setw(5) << std::setfill('0')
<< std::internal << std::showpos << exp;
result = oss.str();
return result;
}
int main()
{
std::cout << format(3.99147034531211e-3) << '\n';
std::cout << format(6.02214085774e23) << '\n';
}
Output:
3.99147034531211E-0003
6.02214085774000E+0023
You will need to use std::fixed
Sample program:
#include <iostream>
#include <fstream>
int main()
{
float f1 = -187.33667, f2 = 0.0;
std::ofstream out("test.bin",std::ios_base::binary);
if(out.good())
{
std::cout << "Writing floating point number: " << std::fixed << f1 << std::endl;
out.write((char *)&f1,sizeof(float));
out.close();
}
std::ifstream in("test.bin",std::ios_base::binary);
if(in.good())
{
in.read((char *)&f2,sizeof(float));
std::cout << "Reading floating point number: " << std::fixed << f2 << std::endl;
}
return 0;
}
OP by user Texan40. For more info: Here

atof and stringstream produce different results

I have been looking into a problem whereby I am converting a float to a human readable format, and back. Namely a string. I have ran into issues using stringstream and found that atof produces "better" results.
Notice, I do not print out the data in this case, I used the debugger to retrieve the values:
const char *val = "73.31";
std::stringstream ss;
ss << val << '\0';
float floatVal = 0.0f;
ss >> floatVal; //VALUE IS 73.3100052
floatVal = atof(val); //VALUE IS 73.3099976
There is probably a reasonable explanation to this. If anybody can enlighten me I'd be greatful :).
Answer is based on the assumption that OP uses MSVC
atof is indeed better in reading floating point values than istream.
See this example:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
int main()
{
const char *val = "73.31";
std::stringstream ss;
ss << val;
float floatVal = 0.0f;
ss >> floatVal;
std::cout << "istream>>(float&) :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;
double doubleVal = atof(val);
std::cout << "double atof(const char*) :" << std::setw(18) << std::setprecision(15) << doubleVal << std::endl;
floatVal = doubleVal;
std::cout << "(float)double atof(const char*) :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;
doubleVal = floatVal;
std::cout << "(double)(float)double atof(const char*) :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;
}
Output:
istream>>(float&) : 73.3100051879883
double atof(const char*) : 73.31
(float)double atof(const char*) : 73.3099975585938
(double)(float)double atof(const char*) : 73.3099975585938
The compiler even warns about the conversion from doubleto float this:
warning C4244: '=': conversion from 'double' to 'float', possible loss of data
I also found this page: Conversions from Floating-Point Types
Update:
The value 73.3099975585938 seems to be the correct float interpretation of the double value 73.31.
Update:
istream>>(double&) works correctly as well:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
int main()
{
const char *val = "73.31";
std::stringstream ss;
ss << val;
double doubleVal = 0.0f;
ss >> doubleVal;
std::cout << "istream>>(double&) :" << std::setw(18) << std::setprecision(15) << doubleVal << std::endl;
}
Output:
istream>>(double&) : 73.31
For arithmetic types istream::operator>> uses num_get::get.
num_get::get should be using something like scanf("%g") for float source
BUT:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdlib>
int main()
{
std::string s = "73.31";
float f = 0.f;
sscanf(s.c_str(), "%g", &f);
std::cout << std::setw(18) << std::setprecision(15) << f << std::endl;
}
Output:
73.3099975585938
For me this looks like there might be a bug in Microsoft num_get

C++ How do I stop outputting the decimal and zero of a number like 5.0 using std::cout

I was about to ask this question, and found a few more to ask.
How (according to the top answer) would I then proceed to only display this precision for a single call to cout, and then disable it thereafter?
Say I want to show precision for the first three calls, but not the last:
(I named a variable with the same name as the "fixed" format specifier in order to experiment)
#include <iostream>
int main(){
using namespace std;
int spam = 5;
double flak = 5.0;
double result = spam * flak;
double fixed = 42;
cout.precision(1);
cout << std::fixed << spam + flak << endl;
cout << result << endl;
cout << flak << endl;
cout << fixed;
return 0;
}
You can invoke the precision function again to re-apply it like so:
int main(){
using namespace std;
int spam = 5;
double flak = 5.0;
double result = spam * flak;
double fixed = 42;
cout.precision(1);
cout << std::fixed << spam + flak << endl;
cout << result << endl;
cout << flak << endl;
cout.precision(3);
cout << fixed;
return 0;
}
Another way to do it is to use the <iomanip> header to use the std::setprecision() function and passing it to std::cout, so it would be similar to doing this:
#include <iomanip>
#include <iostream>
int main() {
int spam = 5;
double flak = 5.0;
double result = spam * flak;
double fixed = 42;
std::cout << std::fixed << spam + flak << std::endl;
std::cout << std::setprecision(1) << result << '\n' << flak << std::endl;
std::cout << std::setprecision(0) << fixed;
return 0;
}
Not sure what you want. But C++ provides some ways to control the state of stream.
std::streamsize prec = cout.precision(); // store current precision setting
// some print here
std::cout.precision(prec) // Roll-back
One (ugly) way would be to cast it to int before the cout if you dont want to see the decimal places.
Something like (C style - still ok for primitive types)
cout << (int)flak << endl;
or ( C++ style - recommended, especially for non primitive types like classes)
cout << static_cast<int>(flak) << endl;
This will only change what cout sees, not the variable itself.

how to read binary file content as strings?

I need to read 16 bits from the binary file as std::string or char *. For example, a binary file contains 89 ab cd ef, and I want to be able to extract them as std::strings or char *. I have tried the following code:
ifstream *p = new ifstream();
char *buffer;
p->seekg(address, ios::beg);
buffer = new char[16];
memset(buffer, 0, 16);
p->read(buffer, 16);
When I try to std::cout the buffer, nothing appeared. How can I read these characters in the binary file?
EDIT: I was looking for the buffer to be a int type such as "0x89abcdef". Is it possible to achieve?
Something like:
#include <string>
#include <iostream>
#include <fstream>
#include <iomanip>
int main()
{
if (ifstream input("filename"))
{
std::string s(2 /*bytes*/, '\0' /*initial content - irrelevant*/);
if (input.read(&s[0], 2 /*bytes*/))
std::cout << "SUCCESS: [0] " << std::hex << (int)s[0] << " [1] " << (int)s[1] << '\n';
else
std::cerr << "Couldn't read from file\n";
}
else
std::cerr << "Couldn't open file\n";
}
You can't read a binary stream as though it were text.
You can, of course, read as binary (by using "file.read()" and "file.write()" methods on your stream object). Just like what you're doing now :)
You can also convert binary to text: "convert to hex text string" and "uuencode base 64" are two common ways to do this.
You'll want to read the bytes as numbers (of type long long probably).
Then you can print those using formatting specifiers like this:
#include <iostream>
#include <iomanip>
int main()
{
using namespace std;
int x = 2;
int y = 255;
cout << showbase // show the 0x prefix
<< internal // fill between the prefix and the number
<< setfill('0'); // fill with 0s
cout << hex << setw(4) << x << dec << " = " << setw(3) << x << endl;
cout << hex << setw(4) << y << dec << " = " << setw(3) << y << endl;
return 0;
}