From 42867340779d53c13fc8cdb3b0d1c2084f63883e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 9 Dec 2025 10:20:21 +0100 Subject: [PATCH 1/9] Add reproducer of decimal issue --- test/Jamfile | 1 + test/decimal_github_issue_1260.cpp | 44 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 test/decimal_github_issue_1260.cpp diff --git a/test/Jamfile b/test/Jamfile index 307a099d..015a42e3 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -96,6 +96,7 @@ run ../examples/charconv.cpp ; run limits_link_1.cpp limits_link_2.cpp limits_link_3.cpp ; # Github Issues +run decimal_github_issue_1260.cpp ; run github_issue_207.cpp ; run github_issue_210.cpp ; run github_issue_221.cpp ; diff --git a/test/decimal_github_issue_1260.cpp b/test/decimal_github_issue_1260.cpp new file mode 100644 index 00000000..43825836 --- /dev/null +++ b/test/decimal_github_issue_1260.cpp @@ -0,0 +1,44 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// This only failed with GCC 12+ on 32-bit platforms in release mode +// Debug configuration was fine, as was all other platforms + +#include +#include + +using namespace boost::int128; + +int main() +{ + uint128_t lhs_sig {UINT64_C(54210108624275), UINT64_C(4089650035136921600)}; + uint128_t rhs_sig {UINT64_C(276471553983803), UINT64_C(11633843142343524352)}; + + lhs_sig *= 1000U; + BOOST_TEST_EQ(lhs_sig, uint128_t(UINT64_C(54210108624275221), UINT64_C(12919594847110692864))); + + // 10^31 + rhs_sig /= BOOST_INT128_UINT128_C(10000000000000000000000000000000); + BOOST_TEST_EQ(rhs_sig, 510U); + + rhs_sig /= 10U; + BOOST_TEST_EQ(rhs_sig, 51U); + + auto signed_lhs {static_cast(lhs_sig)}; + auto signed_rhs {static_cast(rhs_sig)}; + signed_rhs = -signed_rhs; + + BOOST_TEST_EQ(signed_rhs, int128_t(INT64_C(-1), UINT64_C(18446744073709551565))); + + auto res_sig {static_cast(signed_lhs + signed_rhs)}; + BOOST_TEST_EQ(res_sig, uint128_t(UINT64_C(54210108624275221), UINT64_C(12919594847110692813))); + + res_sig /= UINT64_C(10); + BOOST_TEST_EQ(res_sig, uint128_t(UINT64_C(5421010862427522), UINT64_C(3136633892082024442))); + + res_sig /= UINT64_C(10); + BOOST_TEST_EQ(res_sig, uint128_t(UINT64_C(542101086242752), UINT64_C(4003012203950112767))); + + return boost::report_errors(); +} From 2ef100d08505472dfdc3aee4f49d8047947587a4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 9 Dec 2025 10:29:05 +0100 Subject: [PATCH 2/9] Removed deprecated runners --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f2168c0..2a945c9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -298,9 +298,6 @@ jobs: source_keys: - "https://apt.llvm.org/llvm-snapshot.gpg.key" - - toolset: clang - cxxstd: "03,11,14,17,20,2b" - os: macos-13 - toolset: clang cxxstd: "03,11,14,17,20,2b" os: macos-14 @@ -623,7 +620,6 @@ jobs: include: - os: ubuntu-22.04 - os: ubuntu-24.04 - - os: macos-13 - os: macos-14 - os: macos-15 @@ -670,7 +666,6 @@ jobs: fail-fast: false matrix: include: - - os: macos-13 - os: macos-14 - os: macos-15 @@ -729,7 +724,6 @@ jobs: include: - os: ubuntu-22.04 - os: ubuntu-24.04 - - os: macos-13 - os: macos-14 - os: macos-15 @@ -786,7 +780,6 @@ jobs: include: - os: ubuntu-22.04 - os: ubuntu-24.04 - - os: macos-13 - os: macos-14 - os: macos-15 @@ -959,7 +952,6 @@ jobs: matrix: include: - os: ubuntu-24.04 - - os: macos-13 - os: macos-14 - os: macos-15 From 64c40efe54c239c1b78b662f81275c1b9566f080 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 9 Dec 2025 13:15:05 +0100 Subject: [PATCH 3/9] Add lldb pretty printer --- extra/int128_printer.py | 115 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 extra/int128_printer.py diff --git a/extra/int128_printer.py b/extra/int128_printer.py new file mode 100644 index 00000000..ade3095c --- /dev/null +++ b/extra/int128_printer.py @@ -0,0 +1,115 @@ +# int128_printer.py +# +# Struct definitions: +# struct uint128_t { std::uint64_t low; std::uint64_t high; }; +# struct int128_t { std::uint64_t low; std::int64_t high; }; +# +# On big endian machines the word order is reversed + +import lldb + +def uint128_summary(valobj, internal_dict): + """ + Custom summary for uint128_t type (unsigned). + Displays as decimal (base 10). + """ + try: + val = valobj.GetNonSyntheticValue() + high = val.GetChildMemberWithName("high").GetValueAsUnsigned() + low = val.GetChildMemberWithName("low").GetValueAsUnsigned() + + value = (high << 64) | low + return str(value) + except Exception as e: + return f"" + +def int128_summary(valobj, internal_dict): + """ + Custom summary for int128_t type (signed). + Displays as decimal (base 10). + """ + try: + val = valobj.GetNonSyntheticValue() + # high is std::int64_t, so use GetValueAsSigned() + high = val.GetChildMemberWithName("high").GetValueAsSigned() + # low is std::uint64_t, so use GetValueAsUnsigned() + low = val.GetChildMemberWithName("low").GetValueAsUnsigned() + + value = (high << 64) + low + return str(value) + except Exception as e: + return f"" + +def __lldb_init_module(debugger, internal_dict): + uint128_pattern = r"^(const )?(boost::int128::uint128_t|(\w+::)*uint128_t)( &| \*)?$" + int128_pattern = r"^(const )?(boost::int128::int128_t|(\w+::)*int128_t)( &| \*)?$" + + debugger.HandleCommand( + f'type summary add -x "{uint128_pattern}" -e -F int128_printer.uint128_summary' + ) + debugger.HandleCommand( + f'type synthetic add -x "{uint128_pattern}" -l int128_printer.Uint128SyntheticProvider' + ) + + debugger.HandleCommand( + f'type summary add -x "{int128_pattern}" -e -F int128_printer.int128_summary' + ) + debugger.HandleCommand( + f'type synthetic add -x "{int128_pattern}" -l int128_printer.Int128SyntheticProvider' + ) + + print("int128_t and uint128_t pretty printers loaded successfully") + +class Uint128SyntheticProvider: + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def num_children(self): + return 2 + + def get_child_index(self, name): + if name == "low": + return 0 + elif name == "high": + return 1 + return -1 + + def get_child_at_index(self, index): + if index == 0: + return self.valobj.GetChildMemberWithName("low") + elif index == 1: + return self.valobj.GetChildMemberWithName("high") + return None + + def update(self): + pass + + def has_children(self): + return True + +class Int128SyntheticProvider: + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def num_children(self): + return 2 + + def get_child_index(self, name): + if name == "low": + return 0 + elif name == "high": + return 1 + return -1 + + def get_child_at_index(self, index): + if index == 0: + return self.valobj.GetChildMemberWithName("low") + elif index == 1: + return self.valobj.GetChildMemberWithName("high") + return None + + def update(self): + pass + + def has_children(self): + return True From b7e5ee340534d88d50e984bc6127eb6afbfd00ab Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 9 Dec 2025 13:23:31 +0100 Subject: [PATCH 4/9] Document how to use the pretty printer module --- doc/int128.adoc | 2 ++ doc/int128/printer.adoc | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 doc/int128/printer.adoc diff --git a/doc/int128.adoc b/doc/int128.adoc index 30806c28..b8128044 100644 --- a/doc/int128.adoc +++ b/doc/int128.adoc @@ -20,6 +20,8 @@ Matt Borland include::int128/overview.adoc[] +include::int128/printer.adoc[] + include::int128/api_reference.adoc[] include::int128/file_structure.adoc[] diff --git a/doc/int128/printer.adoc b/doc/int128/printer.adoc new file mode 100644 index 00000000..9b8df8e1 --- /dev/null +++ b/doc/int128/printer.adoc @@ -0,0 +1,21 @@ +//// +Copyright 2025 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#pretty_printer] += Pretty Printers +:idprefix: pretty_printers_ + +The library contains a pretty printer for LLDB in the `extra/` folder. +To use this, add the following line to your `~/.lldbinit` file: + +[source] +---- +command script import /path/to/int128/extra/int128_printer.py +---- + +If this is successful, you should see the following message in your debugger upon startup: + +"int128_t and uint128_t pretty printers loaded successfully" From 90386cce460e0007af301601c13b280a149070ff Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 9 Dec 2025 14:41:12 +0100 Subject: [PATCH 5/9] Add thousands seperators --- extra/int128_printer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/int128_printer.py b/extra/int128_printer.py index ade3095c..bf27cf63 100644 --- a/extra/int128_printer.py +++ b/extra/int128_printer.py @@ -19,7 +19,7 @@ def uint128_summary(valobj, internal_dict): low = val.GetChildMemberWithName("low").GetValueAsUnsigned() value = (high << 64) | low - return str(value) + return f"{value:,}" except Exception as e: return f"" @@ -36,7 +36,7 @@ def int128_summary(valobj, internal_dict): low = val.GetChildMemberWithName("low").GetValueAsUnsigned() value = (high << 64) + low - return str(value) + return f"{value:,}" except Exception as e: return f"" From ab099d39d82e03132eb3a297a804cb0461d342d0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 11 Dec 2025 10:00:50 +0100 Subject: [PATCH 6/9] Rename so we can add gdb --- extra/{int128_printer.py => int128_printer_lldb.py} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename extra/{int128_printer.py => int128_printer_lldb.py} (96%) diff --git a/extra/int128_printer.py b/extra/int128_printer_lldb.py similarity index 96% rename from extra/int128_printer.py rename to extra/int128_printer_lldb.py index bf27cf63..1fe4fea3 100644 --- a/extra/int128_printer.py +++ b/extra/int128_printer_lldb.py @@ -1,4 +1,6 @@ -# int128_printer.py +# Copyright 2025 Matt Borland +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt # # Struct definitions: # struct uint128_t { std::uint64_t low; std::uint64_t high; }; From a54089869e86c4268a2759d164769f45179ce2fa Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 11 Dec 2025 10:04:06 +0100 Subject: [PATCH 7/9] Add gdb printer --- extra/int128_printer_gdb.py | 116 ++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 extra/int128_printer_gdb.py diff --git a/extra/int128_printer_gdb.py b/extra/int128_printer_gdb.py new file mode 100644 index 00000000..360990a9 --- /dev/null +++ b/extra/int128_printer_gdb.py @@ -0,0 +1,116 @@ +# Copyright 2025 Matt Borland +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt +# +# Struct definitions: +# struct uint128_t { std::uint64_t low; std::uint64_t high; }; +# struct int128_t { std::uint64_t low; std::int64_t high; }; +# +# On big endian machines the word order is reversed +# +# Usage: source int128_printer.py + +import gdb +import gdb.printing +import re + +class Uint128Printer: + """Pretty printer for uint128_t type""" + + def __init__(self, val): + self.val = val + + def to_string(self): + try: + high = int(self.val["high"]) & 0xFFFFFFFFFFFFFFFF # Treat as unsigned + low = int(self.val["low"]) & 0xFFFFFFFFFFFFFFFF + + value = (high << 64) | low + return f"{value:,}" + except Exception as e: + return f"" + + def children(self): + yield "low", self.val["low"] + yield "high", self.val["high"] + + def display_hint(self): + return None + + +class Int128Printer: + """Pretty printer for int128_t type""" + + def __init__(self, val): + self.val = val + + def to_string(self): + try: + # high is std::int64_t (signed) + high = int(self.val["high"]) + # Ensure high is treated as signed 64-bit + if high >= 0x8000000000000000: + high -= 0x10000000000000000 + + # low is std::uint64_t (unsigned) + low = int(self.val["low"]) & 0xFFFFFFFFFFFFFFFF + + value = (high << 64) + low + return f"{value:,}" + except Exception as e: + return f"" + + def children(self): + yield "low", self.val["low"] + yield "high", self.val["high"] + + def display_hint(self): + return None + + +def lookup_int128_type(val): + """ + Lookup function to detect if a type should use our pretty printers. + Returns the appropriate printer or None. + """ + # Get the basic type name, stripping references and const qualifiers + type_obj = val.type + + # Handle references and pointers + if type_obj.code == gdb.TYPE_CODE_REF: + type_obj = type_obj.target() + if type_obj.code == gdb.TYPE_CODE_PTR: + return None # Don't handle pointers directly + + # Strip const/volatile qualifiers + type_obj = type_obj.unqualified() + + type_name = str(type_obj) + + # Patterns to match uint128_t and int128_t types + uint128_pattern = re.compile( + r"^(boost::int128::uint128_t|(\w+::)*uint128_t|uint128_t)$" + ) + int128_pattern = re.compile( + r"^(boost::int128::int128_t|(\w+::)*int128_t|int128_t)$" + ) + + if uint128_pattern.match(type_name): + return Uint128Printer(val) + if int128_pattern.match(type_name): + return Int128Printer(val) + + return None + + +def register_int128_printers(objfile=None): + """Register the int128 pretty printers.""" + if objfile is None: + objfile = gdb + + objfile.pretty_printers.append(lookup_int128_type) + + +# Auto-register when the script is sourced +register_int128_printers() +print("int128_t and uint128_t pretty printers loaded successfully") From 8c9b1a71e77059d781dc5075744825dc26736ba0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 11 Dec 2025 10:17:27 +0100 Subject: [PATCH 8/9] Fix lldb path --- extra/int128_printer_lldb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extra/int128_printer_lldb.py b/extra/int128_printer_lldb.py index 1fe4fea3..40954e84 100644 --- a/extra/int128_printer_lldb.py +++ b/extra/int128_printer_lldb.py @@ -47,17 +47,17 @@ def __lldb_init_module(debugger, internal_dict): int128_pattern = r"^(const )?(boost::int128::int128_t|(\w+::)*int128_t)( &| \*)?$" debugger.HandleCommand( - f'type summary add -x "{uint128_pattern}" -e -F int128_printer.uint128_summary' + f'type summary add -x "{uint128_pattern}" -e -F int128_printer_lldb.uint128_summary' ) debugger.HandleCommand( - f'type synthetic add -x "{uint128_pattern}" -l int128_printer.Uint128SyntheticProvider' + f'type synthetic add -x "{uint128_pattern}" -l int128_printer_lldb.Uint128SyntheticProvider' ) debugger.HandleCommand( - f'type summary add -x "{int128_pattern}" -e -F int128_printer.int128_summary' + f'type summary add -x "{int128_pattern}" -e -F int128_printer_lldb.int128_summary' ) debugger.HandleCommand( - f'type synthetic add -x "{int128_pattern}" -l int128_printer.Int128SyntheticProvider' + f'type synthetic add -x "{int128_pattern}" -l int128_printer_lldb.Int128SyntheticProvider' ) print("int128_t and uint128_t pretty printers loaded successfully") From 53b419fda588496b1ef4503949ae44b8bccc53f3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 7 Jan 2026 15:42:23 -0500 Subject: [PATCH 9/9] Fix strict aliasing violation --- include/boost/int128/detail/uint128_imp.hpp | 24 ++++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/include/boost/int128/detail/uint128_imp.hpp b/include/boost/int128/detail/uint128_imp.hpp index a0df7831..6a555819 100644 --- a/include/boost/int128/detail/uint128_imp.hpp +++ b/include/boost/int128/detail/uint128_imp.hpp @@ -677,8 +677,10 @@ BOOST_INT128_EXPORT constexpr bool operator<(const uint128_t lhs, const uint128_ } else { - const uint32_t* l = reinterpret_cast(&lhs); - const uint32_t* r = reinterpret_cast(&rhs); + std::uint32_t l[4] {}; + std::uint32_t r[4] {}; + std::memcpy(l, &lhs, sizeof(lhs)); + std::memcpy(r, &rhs, sizeof(rhs)); if (l[3] != r[3]) { @@ -830,8 +832,10 @@ BOOST_INT128_EXPORT constexpr bool operator<=(const uint128_t lhs, const uint128 } else { - const uint32_t* l = reinterpret_cast(&lhs); - const uint32_t* r = reinterpret_cast(&rhs); + std::uint32_t l[4] {}; + std::uint32_t r[4] {}; + std::memcpy(l, &lhs, sizeof(lhs)); + std::memcpy(r, &rhs, sizeof(rhs)); if (l[3] != r[3]) { @@ -983,8 +987,10 @@ BOOST_INT128_EXPORT constexpr bool operator>(const uint128_t lhs, const uint128_ } else { - const uint32_t* l = reinterpret_cast(&lhs); - const uint32_t* r = reinterpret_cast(&rhs); + std::uint32_t l[4] {}; + std::uint32_t r[4] {}; + std::memcpy(l, &lhs, sizeof(lhs)); + std::memcpy(r, &rhs, sizeof(rhs)); if (l[3] != r[3]) { @@ -1136,8 +1142,10 @@ BOOST_INT128_EXPORT constexpr bool operator>=(const uint128_t lhs, const uint128 } else { - const uint32_t* l = reinterpret_cast(&lhs); - const uint32_t* r = reinterpret_cast(&rhs); + std::uint32_t l[4] {}; + std::uint32_t r[4] {}; + std::memcpy(l, &lhs, sizeof(lhs)); + std::memcpy(r, &rhs, sizeof(rhs)); if (l[3] != r[3]) {