Skip to content

Scalar-constant emitter truncates doubles to 6 significant digits — silently wrong constants in generated code #131

Open

Description

@petlenz

From the 2026-08-02 full-repo review (finding F-02, verified against source at cfafaf1).

Evidence: include/numsim_codegen/code_emit/scalar_code_emit.h:76-85 — the plain-double branch of operator()(cas::scalar_constant const&) uses std::ostringstream os; os << static_cast<double>(val); (default precision = 6 significant digits, not round-trip exact). The std::complex branch (lines 68-71) has the same problem. E.g. 1.0/3.0 emits 0.333333; a fitted coefficient 0.1234567890123456 emits 0.123457.

Contrast: parameter defaults were already hardened with round-trip-exact std::format("{}", v) (src/targets/moose_material.cpp:379, src/targets/numsim_material.cpp:71 fmt()). The fix landed for that call site but missed the general scalar-constant emitter that every target routes through. Currently masked in tests because rational constants go through the exact num.0 / den.0 branch and no test seeds a >6-digit plain double.

Impact: any recipe embedding a non-"nice" scalar_constant bakes a silently truncated physics constant into generated C++ — compiles fine, wrong results.

Fix: replace the else branch with std::format("{}", static_cast<double>(val)) (keep the .0-suffix logic); same for the complex branch. Regression test: emit a recipe with 0.1234567890123456, strtod the emitted literal, assert exact round-trip.

Related: #22 (general std::format migration — this is the one correctness-critical site).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions