From 71afa3e0fc7d3b7677908e850565407af45e6b9a Mon Sep 17 00:00:00 2001 From: Mike Alexander Date: Thu, 18 May 2023 17:42:44 -0400 Subject: [PATCH] Make the GncNumeric string constructtor work for long decimal numbers. It was failing to produce a correct representation for input with more than 14 digits after the decimal poin. Fixes Bug 798916. The bug was in powten so this fix may corect other problems too. For example conversion of GncNumeric back to a string also failed in certain circumsances. --- libgnucash/engine/gnc-numeric.cpp | 17 +++++++++++++---- libgnucash/engine/test/gtest-gnc-numeric.cpp | 6 ++++++ libgnucash/engine/test/test-numeric.cpp | 4 ++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp index d0e716c295..7ab1ed2063 100644 --- a/libgnucash/engine/gnc-numeric.cpp +++ b/libgnucash/engine/gnc-numeric.cpp @@ -48,9 +48,9 @@ static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, INT64_C(10000000000), INT64_C(100000000000), INT64_C(1000000000000), INT64_C(10000000000000), INT64_C(100000000000000), + INT64_C(1000000000000000), INT64_C(10000000000000000), - INT64_C(100000000000000000), - INT64_C(1000000000000000000)}; + INT64_C(100000000000000000)}; #define POWTEN_OVERFLOW -5 int64_t @@ -175,9 +175,18 @@ GncNumeric::GncNumeric(const std::string& str, bool autoround) GncInt128 high((neg && m[1].length() > 1) || (!neg && m[1].length()) ? stoll(m[1].str()) : 0); GncInt128 low(stoll(m[2].str())); - int64_t d = powten(m[2].str().length()); + auto exp10 = m[2].str().length(); + int64_t d = powten(exp10); GncInt128 n = high * d + (neg ? -low : low); - + while (exp10 > max_leg_digits) + { + /* If the arg to powten is bigger than max_leg_digits + it returns 10**max_leg_digits so reduce exp10 by + that amount */ + n = n / powten(exp10 - max_leg_digits); + exp10 -= max_leg_digits; + } + if (!autoround && n.isBig()) { std::ostringstream errmsg; diff --git a/libgnucash/engine/test/gtest-gnc-numeric.cpp b/libgnucash/engine/test/gtest-gnc-numeric.cpp index e194367bbf..27b386748b 100644 --- a/libgnucash/engine/test/gtest-gnc-numeric.cpp +++ b/libgnucash/engine/test/gtest-gnc-numeric.cpp @@ -185,6 +185,12 @@ TEST(gncnumeric_constructors, test_string_constructor) GncNumeric neg_decimal_frac_nozero("-.12345"); EXPECT_EQ(-12345, neg_decimal_frac_nozero.num()); EXPECT_EQ(100000, neg_decimal_frac_nozero.denom()); + GncNumeric big_denom(".12345678901234567"); + EXPECT_EQ(12345678901234567, big_denom.num()); + EXPECT_EQ(100000000000000000, big_denom.denom()); + GncNumeric too_big_denom(".123456789012345678"); + EXPECT_EQ(12345678901234567, too_big_denom.num()); + EXPECT_EQ(100000000000000000, too_big_denom.denom()); } TEST(gncnumeric_output, string_output) diff --git a/libgnucash/engine/test/test-numeric.cpp b/libgnucash/engine/test/test-numeric.cpp index 41ef28fa08..bf3cd51dd7 100644 --- a/libgnucash/engine/test/test-numeric.cpp +++ b/libgnucash/engine/test/test-numeric.cpp @@ -545,8 +545,8 @@ check_add_subtract (void) static const gint64 pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, - 100000000000000, 10000000000000000, - 100000000000000000, 1000000000000000000}; + 100000000000000, 1000000000000000, + 10000000000000000, 100000000000000000}; #define POWTEN_OVERFLOW -5 static inline gint64