From 3b80f573ee4b047950460baefc0a003535a448f7 Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Fri, 30 Oct 2026 08:44:27 +1100 Subject: [PATCH] [gnc-numeric.hpp] Keep the sign and leading zeroes in decimal output operator<< printed the integer and fractional parts with separate integer divisions, so 5/100 came out as "0.5", 105/100 as "1.5" and -1/100 as "0.1": leading zeroes of the fraction were dropped and values between -1 and 0 lost their sign. Print the magnitude and write each fractional digit so the output matches the value. --- libgnucash/engine/gnc-numeric.hpp | 15 +++++++++++-- libgnucash/engine/test/gtest-gnc-numeric.cpp | 23 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/libgnucash/engine/gnc-numeric.hpp b/libgnucash/engine/gnc-numeric.hpp index fb532f263d..a4bcdf360b 100644 --- a/libgnucash/engine/gnc-numeric.hpp +++ b/libgnucash/engine/gnc-numeric.hpp @@ -363,8 +363,19 @@ std::basic_ostream& operator<<(std::basic_ostream& if (n.denom() == 1) ss << n.num(); else if (n.is_decimal()) - ss << n.num() / n.denom() << dec_pt - << (n.num() > 0 ? n.num() : -n.num()) % n.denom(); + { + /* Use the magnitude so that values between -1 and 0 keep their sign, + * and write each fractional digit so that leading zeroes are kept. */ + auto num = n.num(); + auto mag = num < 0 ? 0 - static_cast(num) + : static_cast(num); + auto den = static_cast(n.denom()); + if (num < 0) + ss << static_cast('-'); + ss << mag / den << dec_pt; + for (auto place = den / 10; place > 0; place /= 10) + ss << static_cast('0' + mag % den / place % 10); + } else ss << n.num() << "/" << n.denom(); s << ss.str(); diff --git a/libgnucash/engine/test/gtest-gnc-numeric.cpp b/libgnucash/engine/test/gtest-gnc-numeric.cpp index d9fea79f29..796ed2b697 100644 --- a/libgnucash/engine/test/gtest-gnc-numeric.cpp +++ b/libgnucash/engine/test/gtest-gnc-numeric.cpp @@ -328,6 +328,29 @@ TEST(gncnumeric_stream, output_stream) EXPECT_EQ("123/456", output.str()); } +TEST(gncnumeric_stream, output_stream_decimal_places) +{ + std::ostringstream output; + output << GncNumeric(5, 100); + EXPECT_EQ("0.05", output.str()); + output.str(""); + output << GncNumeric(105, 100); + EXPECT_EQ("1.05", output.str()); + output.str(""); + output << GncNumeric(-105, 100); + EXPECT_EQ("-1.05", output.str()); + output.str(""); + output << GncNumeric(-1, 100); + EXPECT_EQ("-0.01", output.str()); + output.str(""); + output << GncNumeric(-5, 10); + EXPECT_EQ("-0.5", output.str()); + output.str(""); + output << GncNumeric(INT64_MIN, 1000); + EXPECT_EQ("-9223372036854775.808", output.str()); + EXPECT_EQ("0.007", GncNumeric(7, 1000).to_string()); +} + TEST(gncnumeric_stream, input_stream) { std::istringstream input("123456");