Skip to content

Wrong doubles parsed under MSVC Release (/O1 /GL /LTCG). #880

Description

@ATHASBNK

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

volatile uint64_t vf = v.f; v.f = vf;
volatile int      ve = v.e; v.e = ve;

Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35728 for x64

---- CODE------



//
// 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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions