-
Notifications
You must be signed in to change notification settings - Fork 797
Issue when serializing double #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Currently the only way you can address this in cereal without modifying the JSON code is by adjusting the precision parameter for If you want different behavior you would need to adjust the printf format string For XML output, we use an |
I had the same problem and updated rapidjson to solve the problem. This now includes a new flag EDIT: I also don't specify the output precision at all (removed this from json.hpp). I think this now defaults to full precision, but actually outputs the number in a clever way, using as many decimal places as required. |
Good to know, another reason for us to get a move on with #82. |
Can this be a problem using the binary archive? Or that will use a bit-wise identical representation of floats? |
The binary archives serialize the bitwise representation so this is not an issue there. |
Serializing using a binary archive should save the raw binary representation of the double (base64 encoded), and therefore give a bit-wise identical representation when unserializing. Saving as a decimal representation involves a conversion from a base-2 representation to a base-10 representation, which is necessarily inexact for some numbers, and therefore involves rounding. The same is true for the conversion back from base-10 to base-2. This can be avoided by using a hexadecimal representation for floats, but this is not yet a standard format. For serialization purposes, I would argue that one wants the transformation
to result in exactly the same double. This can be achieved using a decimal representation, but is tricky:
Both of these (1.ii and 2) are done correctly in recent versions of rapidjson when using the |
Yip, doing a roundtrip (serialize and then deserialize) of double fails: #include <cereal/archives/json.hpp>
#include <fstream>
#include <iomanip>
#include <cassert>
#include <sstream>
struct MyRecord
{
double z; // here is the double
template <class Archive>
void serialize( Archive & ar )
{
ar( z );
}
};
int main()
{
double arr[] = {2.2};
for (const auto v : arr) {
MyRecord rec{v};
// serialize
{
std::ofstream os("out.cereal");
cereal::JSONOutputArchive archive_out( os );
archive_out( rec );
}
// deserialize
{
MyRecord rec2;
{
std::ifstream is("out.cereal");
cereal::JSONInputArchive archive_in( is );
cereal::JSONOutputArchive archive_out( std::cout );
archive_in( rec2 );
archive_out( rec2 );
}
// print status
std::cout << std::endl;
std::cout << "original : " << std::setprecision(std::numeric_limits<double>::max_digits10)
<< rec.z
<< "\t(printed with std::setprecision(std::numeric_limits<double>::max_digits10))" << std::endl;
std::cout << "after roundtrip: " << std::setprecision(std::numeric_limits<double>::max_digits10)
<< rec2.z
<< "\t(printed with std::setprecision(std::numeric_limits<double>::max_digits10))" << std::endl;
assert(rec.z == rec2.z); // FAILS: round-trip does not work!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
std::cout << std::endl;
}
return 0;
} It does however seem to work when using Note: a correct way of serializing floats and double is by using struct MyRecord
{
double z; // here is the double
};
MyRecord rec{2.2};
{
std::stringstream ss;
ss << std::setprecision(std::numeric_limits<double>::max_digits10)
<< rec.z; // serialize from double to string
decltype(rec.z) val;
ss >> val; // deserialize back to double
assert(val == rec.z); // round-trip perfect
} |
For example x is a double and equal to 3545.45, but after serialization, it become 3545.4500000000007. This makes trouble when I use the serialized data(xml/json) in other places.
I found this is a common issue for all double. Is there any way to avoid this?
I want the result is exactly like:
Or I can control the format like printf. If I want to three decimals, it is like:
Thanks in advance.
The text was updated successfully, but these errors were encountered: