From 2b7375e7107df233d0cbe8529260fd467243b335 Mon Sep 17 00:00:00 2001 From: Simon Gene Gottlieb Date: Thu, 16 Oct 2025 15:06:49 +0200 Subject: [PATCH] feat: Emitter has option to produce string with trailing '.0' for floating point value This adds a new function Emitter::SetShowTrailingZero(bool). By setting this, floating point values like '0' or '5' will get an extra '.0' attached to force others to parse them as floats. Example: ``` YAML::Emitter emitter; emitter.SetShowTrailingZero(true); emitter << .... ``` Fix #226 #412 #1016 Co-Author: Romain Reignier --- include/yaml-cpp/emitter.h | 13 +++++++++- src/emitter.cpp | 8 ++++++ src/emitterstate.cpp | 6 +++++ src/emitterstate.h | 3 +++ test/integration/emitter_test.cpp | 42 +++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/include/yaml-cpp/emitter.h b/include/yaml-cpp/emitter.h index 67810ef34..2c5af34c7 100644 --- a/include/yaml-cpp/emitter.h +++ b/include/yaml-cpp/emitter.h @@ -65,6 +65,7 @@ class YAML_CPP_API Emitter { bool SetPostCommentIndent(std::size_t n); bool SetFloatPrecision(std::size_t n); bool SetDoublePrecision(std::size_t n); + bool SetShowTrailingZero(bool value); void RestoreGlobalModifiedSettings(); // local setters @@ -95,6 +96,7 @@ class YAML_CPP_API Emitter { void SetStreamablePrecision(std::stringstream&) {} std::size_t GetFloatPrecision() const; std::size_t GetDoublePrecision() const; + bool GetShowTrailingZero() const; void PrepareIntegralStream(std::stringstream& stream) const; void StartedScalar(); @@ -187,8 +189,17 @@ inline Emitter& Emitter::WriteStreamable(T value) { } if (!special) { - stream << FpToString(value, stream.precision()); + auto value_as_str = FpToString(value, stream.precision()); + if (GetShowTrailingZero()) { + bool isInScientificNotation = (value_as_str.find('e') != std::string::npos); + bool hasDot = (value_as_str.find('.') != std::string::npos); + if (!isInScientificNotation && !hasDot) { + value_as_str += ".0"; + } + } + stream << value_as_str; } + m_stream << stream.str(); StartedScalar(); diff --git a/src/emitter.cpp b/src/emitter.cpp index e5fc0ea3f..5168cba89 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -90,6 +90,10 @@ bool Emitter::SetDoublePrecision(std::size_t n) { return m_pState->SetDoublePrecision(n, FmtScope::Global); } +bool Emitter::SetShowTrailingZero(bool value) { + return m_pState->SetShowTrailingZero(value, FmtScope::Global); +} + void Emitter::RestoreGlobalModifiedSettings() { m_pState->RestoreGlobalModifiedSettings(); } @@ -764,6 +768,10 @@ std::size_t Emitter::GetDoublePrecision() const { return m_pState->GetDoublePrecision(); } +bool Emitter::GetShowTrailingZero() const { + return m_pState->GetShowTrailingZero(); +} + const char* Emitter::ComputeFullBoolName(bool b) const { const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index 3dbe40110..5dfad04f2 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -23,6 +23,7 @@ EmitterState::EmitterState() m_mapKeyFmt(Auto), m_floatPrecision(std::numeric_limits::max_digits10), m_doublePrecision(std::numeric_limits::max_digits10), + m_showTrailingZero(false), // m_modifiedSettings{}, m_globalModifiedSettings{}, @@ -397,4 +398,9 @@ bool EmitterState::SetDoublePrecision(std::size_t value, _Set(m_doublePrecision, value, scope); return true; } +bool EmitterState::SetShowTrailingZero(bool value, FmtScope::value scope) { + _Set(m_showTrailingZero, value, scope); + return true; +} + } // namespace YAML diff --git a/src/emitterstate.h b/src/emitterstate.h index 8f379ca95..351a09622 100644 --- a/src/emitterstate.h +++ b/src/emitterstate.h @@ -119,6 +119,8 @@ class EmitterState { std::size_t GetFloatPrecision() const { return m_floatPrecision.get(); } bool SetDoublePrecision(std::size_t value, FmtScope::value scope); std::size_t GetDoublePrecision() const { return m_doublePrecision.get(); } + bool SetShowTrailingZero(bool value, FmtScope::value scope); + bool GetShowTrailingZero() const { return m_showTrailingZero.get(); } private: template @@ -146,6 +148,7 @@ class EmitterState { Setting m_mapKeyFmt; Setting m_floatPrecision; Setting m_doublePrecision; + Setting m_showTrailingZero; SettingChanges m_modifiedSettings; SettingChanges m_globalModifiedSettings; diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index 4bfc136df..f1472d6f3 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -1780,5 +1780,47 @@ TEST_F(EmitterErrorTest, InvalidAlias) { ExpectEmitError(ErrorMsg::INVALID_ALIAS); } +TEST_F(EmitterTest, ShowTrailingZero) { + out << BeginSeq; + out.SetShowTrailingZero(false); + out << 0.; + out << -0.; + out << 3.; + out << 42.; + out.SetShowTrailingZero(true); + out << 0.; + out << -0.; + out << 4.; + out << 51.; + out.SetShowTrailingZero(false); + out << 0.2; + out << 5.12; + out.SetShowTrailingZero(true); + out << 0.2; + out << 6.34; + out << std::numeric_limits::infinity(); + out << -std::numeric_limits::infinity(); + out << std::numeric_limits::quiet_NaN(); + out << std::numeric_limits::signaling_NaN(); + out << EndSeq; + + ExpectEmit(R"(- 0 +- -0 +- 3 +- 42 +- 0.0 +- -0.0 +- 4.0 +- 51.0 +- 0.2 +- 5.12 +- 0.2 +- 6.34 +- .inf +- -.inf +- .nan +- .nan)"); +} + } // namespace } // namespace YAML