gnucash stable: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Fri Mar 7 15:57:36 EST 2025
Updated via https://github.com/Gnucash/gnucash/commit/1fa4f71f (commit)
via https://github.com/Gnucash/gnucash/commit/058e2a19 (commit)
from https://github.com/Gnucash/gnucash/commit/9172d1dd (commit)
commit 1fa4f71f59585665202a206b4175a5e0866beaf8
Author: John Ralls <jralls at ceridwen.us>
Date: Fri Mar 7 12:54:36 2025 -0800
Fix gnc-numeric test failure on macOS 15.4 beta.
Caused by Apple suddenly fixing ostreams to put the thousands
separator in numbers when it's set in the locale.
diff --git a/libgnucash/engine/test/gtest-gnc-numeric.cpp b/libgnucash/engine/test/gtest-gnc-numeric.cpp
index 12f301463d..58bd1162ca 100644
--- a/libgnucash/engine/test/gtest-gnc-numeric.cpp
+++ b/libgnucash/engine/test/gtest-gnc-numeric.cpp
@@ -24,6 +24,9 @@
#include <cstdint>
#include "../gnc-numeric.hpp"
#include "../gnc-rational.hpp"
+#ifdef __APPLE__
+#include <sys/sysctl.h>
+#endif
TEST(gncnumeric_constructors, test_default_constructor)
{
@@ -255,6 +258,23 @@ TEST(gncnumeric_output, string_output)
EXPECT_EQ("-123/456", neg_rational_string.to_string());
}
+#ifdef __APPLE__
+static float
+get_macos_version()
+{
+ int major, minor, micro;
+ float rv{};
+ char vstr[64];
+ size_t len = sizeof(vstr);
+ auto err = sysctlbyname("kern.osrelease", vstr, &len, nullptr, 0);
+ if (err)
+ return rv;
+ sscanf(vstr, "%d.%d.%d", &major, &minor, µ);
+ rv = major + minor / 100.0 + micro / 10000.0;
+ return rv;
+}
+#endif
+
TEST(gncnumeric_stream, output_stream)
{
std::ostringstream output;
@@ -286,6 +306,12 @@ TEST(gncnumeric_stream, output_stream)
output.imbue(std::locale("fr_FR"));
output.str("");
output << simple_int;
+// Apple fixed this in macOS 15.4 to include the separator.
+#ifdef __APPLE__
+ if (get_macos_version() >= 24.04)
+ EXPECT_EQ("123\xe2\x80\xaf""456", output.str());
+ else
+#endif
EXPECT_EQ("123456", output.str());
}
output.str("");
commit 058e2a196b39814e343dd606a9f9f0d3d8e20704
Author: John Ralls <jralls at ceridwen.us>
Date: Fri Mar 7 11:50:22 2025 -0800
Bug 799564 - Decimal point confusions when getting stock quotes from aex
Add parsing thousands-grouped numbers to GncNumeric(const std::string&).
diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp
index 9a0ea98c92..62aacfaa60 100644
--- a/libgnucash/engine/gnc-numeric.cpp
+++ b/libgnucash/engine/gnc-numeric.cpp
@@ -242,24 +242,37 @@ fast_numeral_rational (const char* str)
}
GncNumeric::GncNumeric(const std::string &str, bool autoround) {
+ static const std::string begin("^[^-.0-9]*");
+ static const std::string end("[^0-9]*$");
+ static const std::string begin_group("(?:");
+ static const std::string end_group(")");
+ static const std::string or_op("|");
static const std::string maybe_sign ("(-?)");
static const std::string opt_signed_int("(-?[0-9]*)");
+ static const std::string opt_signed_separated_int("(-?[0-9]{1,3})");
static const std::string unsigned_int("([0-9]+)");
+ static const std::string eu_separated_int("(?:[\\s'.]([0-9]{3}))?");
+ static const std::string en_separated_int("(?:\\,([0-9]{3}))?");
+ static const std::string eu_decimal_part("(?:\\,([0-9]+))?");
+ static const std::string en_decimal_part("(?:\\.([0-9]+))?");
static const std::string hex_frag("(0[xX][A-Fa-f0-9]+)");
static const std::string slash("[ \\t]*/[ \\t]*");
static const std::string whitespace("[ \\t]+");
+ static const std::string eu_sep_decimal(begin_group + opt_signed_separated_int + eu_separated_int + eu_separated_int + eu_separated_int + eu_separated_int + eu_decimal_part + end_group);
+ static const std::string en_sep_decimal(begin_group + opt_signed_separated_int + en_separated_int + en_separated_int + en_separated_int + en_separated_int + en_decimal_part + end_group);
/* The llvm standard C++ library refused to recognize the - in the
* opt_signed_int pattern with the default ECMAScript syntax so we use the
* awk syntax.
*/
- static const regex numeral(opt_signed_int);
- static const regex hex(hex_frag);
- static const regex numeral_rational(opt_signed_int + slash + unsigned_int);
- static const regex integer_and_fraction(maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int);
- static const regex hex_rational(hex_frag + slash + hex_frag);
- static const regex hex_over_num(hex_frag + slash + unsigned_int);
- static const regex num_over_hex(opt_signed_int + slash + hex_frag);
- static const regex decimal(opt_signed_int + "[.,]" + unsigned_int);
+ static const regex numeral(begin + opt_signed_int + end);
+ static const regex hex(begin + hex_frag + end);
+ static const regex numeral_rational(begin + opt_signed_int + slash + unsigned_int + end);
+ static const regex integer_and_fraction(begin + maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int + end);
+ static const regex hex_rational(begin + hex_frag + slash + hex_frag + end);
+ static const regex hex_over_num(begin + hex_frag + slash + unsigned_int + end);
+ static const regex num_over_hex(begin + opt_signed_int + slash + hex_frag + end);
+ static const regex decimal(begin + opt_signed_int + "[.,]" + unsigned_int + end);
+ static const regex sep_decimal(begin + begin_group + eu_sep_decimal + or_op + en_sep_decimal + end_group + end);
static const regex scientific("(?:(-?[0-9]+[.,]?)|(-?[0-9]*)[.,]([0-9]+))[Ee](-?[0-9]+)");
static const regex has_hex_prefix(".*0[xX]$");
smatch m, x;
@@ -327,6 +340,37 @@ GncNumeric::GncNumeric(const std::string &str, bool autoround) {
{
std::string integer{m[1].matched ? m[1].str() : ""};
std::string decimal{m[2].matched ? m[2].str() : ""};
+ auto [num, denom] = reduce_number_pair(numeric_from_decimal_match(integer, decimal), str, autoround);
+ m_num = num;
+ m_den = denom;
+ return;
+ }
+ if (regex_search(str, m, sep_decimal))
+ {
+ /* There's a bit of magic here because of the complexity of
+ * the regex. It supports two formats, one for locales that
+ * use space, apostrophe, or dot for thousands separator and
+ * comma for decimal separator and the other for locales that
+ * use comma for thousands and dot for decimal. For each
+ * format there are 5 captures for thousands-groups (allowing
+ * up to 10^16 - 1) and one for decimal, hence the loops from
+ * 1 - 5 and 7 - 11 with the decimal being either capture 6 or
+ * capture 12.
+ */
+ std::string integer(""), decimal("");
+ for (auto i{1}; i < 6; ++i)
+ if (m[i].matched)
+ integer += m[i].str();
+ if (m[6].matched)
+ decimal += m[6].str();
+ if (integer.empty() && decimal.empty())
+ {
+ for (auto i{7}; i <12; ++i)
+ if (m[i].matched)
+ integer += m[i].str();
+ if (m[12].matched)
+ decimal += m[12].str();
+ }
auto [num, denom] =
reduce_number_pair(numeric_from_decimal_match(integer, decimal),
str, autoround);
diff --git a/libgnucash/engine/test/gtest-gnc-numeric.cpp b/libgnucash/engine/test/gtest-gnc-numeric.cpp
index a38d4b4f25..12f301463d 100644
--- a/libgnucash/engine/test/gtest-gnc-numeric.cpp
+++ b/libgnucash/engine/test/gtest-gnc-numeric.cpp
@@ -141,6 +141,10 @@ TEST(gncnumeric_constructors, test_string_constructor)
GncNumeric neg_simple_decimal("-123.456");
EXPECT_EQ(-123456, neg_simple_decimal.num());
EXPECT_EQ(1000, neg_simple_decimal.denom());
+ ASSERT_NO_THROW(GncNumeric thousep_decimal("123,456,789.123"));
+ GncNumeric thousep_decimal("123,456,789.123");
+ EXPECT_EQ(123456789123, thousep_decimal.num());
+ EXPECT_EQ(1000, thousep_decimal.denom());
ASSERT_NO_THROW(GncNumeric continental_decimal("123,456"));
GncNumeric continental_decimal("123,456");
EXPECT_EQ(123456, continental_decimal.num());
@@ -149,6 +153,10 @@ TEST(gncnumeric_constructors, test_string_constructor)
GncNumeric neg_continental_decimal("-123,456");
EXPECT_EQ(-123456, neg_continental_decimal.num());
EXPECT_EQ(1000, neg_continental_decimal.denom());
+ ASSERT_NO_THROW(GncNumeric swiss_thousep_decimal("123 456 789,123"));
+ GncNumeric swiss_thousep_decimal("123 456 789,123");
+ EXPECT_EQ(123456789123, swiss_thousep_decimal.num());
+ EXPECT_EQ(1000, swiss_thousep_decimal.denom());
GncNumeric from_scientific("1.234e4");
EXPECT_EQ(12340, from_scientific.num());
EXPECT_EQ(1, from_scientific.denom());
Summary of changes:
libgnucash/engine/gnc-numeric.cpp | 60 ++++++++++++++++++++++++----
libgnucash/engine/test/gtest-gnc-numeric.cpp | 34 ++++++++++++++++
2 files changed, 86 insertions(+), 8 deletions(-)
More information about the gnucash-changes
mailing list