cereal parses wrong doubles under MSVC Release (/O1 /GL /LTCG). Values with 17-digit significands come back with ~45% error — e.g. 1.0466 becomes 1.5233.
Debug builds produce correct results.
Reproducer: see below cpp code.
volatile uint64_t vf = v.f; v.f = vf;
volatile int ve = v.e; v.e = ve;
//
// Build (Release — shows the bug):
// cl /nologo /EHsc /O1 /Oi /Ob2 /GL /arch:AVX2 /fp:precise
// /std:c++latest /DNDEBUG /I<cereal>/include StrtodBugRepro.cpp /link /LTCG
//
// Build (Debug — correct):
// cl /nologo /EHsc /Od /I<cereal>/include StrtodBugRepro.cpp
//
#include <cereal/archives/json.hpp>
#include <sstream>
#include <cstdio>
#include <cstring>
#include <cmath>
int main() {
printf("cereal/rapidjson StrtodDiyFp bug — MSVC %d, %s\n\n",
_MSC_VER,
#ifdef NDEBUG
"Release /O1"
#else
"Debug"
#endif
);
static const struct { const char* str; double expected; } tests[] = {
{ "1.0465929998067032", 1.0465929998067032 },
{ "1.0468400282212735", 1.0468400282212735 },
{ "1.0471375609608831", 1.0471375609608831 },
{ "1.0479294350967923", 1.0479294350967923 },
{ "1.0497502792364444", 1.0497502792364444 },
{ "1.0580338136094642", 1.0580338136094642 },
{ "1.0654683335591832", 1.0654683335591832 },
{ "1.0721596486931853", 1.0721596486931853 },
};
int passed = 0, failed = 0;
for (auto& [str, expected] : tests) {
std::string json = std::string("{\"value0\":") + str + "}";
double result = 0.0;
{
std::istringstream is(json);
cereal::JSONInputArchive archive(is);
archive(result);
}
bool ok = (memcmp(&result, &expected, sizeof(double)) == 0);
printf(" parse(\"%s\") = %.17g %s", str, result, ok ? "PASS" : "FAIL");
if (!ok) printf(" (expected %.17g, diff=%.2e)", expected, fabs(result - expected));
printf("\n");
ok ? passed++ : failed++;
}
printf("\n%d PASSED, %d FAILED.\n", passed, failed);
return failed ? 1 : 0;
}
cereal parses wrong doubles under MSVC Release (/O1 /GL /LTCG). Values with 17-digit significands come back with ~45% error — e.g. 1.0466 becomes 1.5233.
Debug builds produce correct results.
Reproducer: see below cpp code.
I found a workaround by adding a volatile barrier in strtod.h
Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35728 for x64
---- CODE------