[libcxx-commits] [libcxx] [libc++] Fix numeric_limits::digits and digits10 for _BitInt(N) (PR #193002)
Xavier Roche via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Apr 20 09:50:51 PDT 2026
https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/193002
>From c8890123c617ce2c2680f8c7a18d2eb9cba0f810 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Mon, 20 Apr 2026 17:48:31 +0200
Subject: [PATCH 1/4] [libc++] Fix numeric_limits::digits and digits10 for
_BitInt(N)
digits was sizeof(T) * CHAR_BIT - is_signed, which counts padding bits
for non-byte-aligned _BitInt(N) widths. unsigned _BitInt(13) reported
digits=16 instead of 13.
Compute it from __builtin_popcountg(~unsigned_type(0)): padding bits
stay zero after bitwise NOT, so the popcount gives the value-bit width.
__numeric_limits_unsigned_type maps signed T to its unsigned counterpart
via __make_unsigned. The old form is the fallback when these builtins
aren't available.
digits10 = digits * 3 / 10 was also wrong for digits >= 256 (gave 76,
should be 77). The new formula digits * 1936274 / 6432163 matches
floor(digits * log10(2)) exactly for every digits in [1, 8388608],
i.e. up to __BITINT_MAXWIDTH__ on x86. Tests cover very wide _BitInt
widths (15437, 70777, 1000000) where coarser rational approximations
of log10(2) would have regressed.
Split out of #185027 at philnik777's request in issue #130584.
Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
libcxx/include/limits | 36 +++++++++++--
.../numeric.limits.members/digits.pass.cpp | 42 ++++++++++++++-
.../numeric.limits.members/digits10.pass.cpp | 54 ++++++++++++++++++-
.../numeric.limits.members/max.pass.cpp | 23 +++++++-
.../numeric.limits.members/min.pass.cpp | 24 ++++++++-
5 files changed, 172 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/limits b/libcxx/include/limits
index ff40d2051d06f..1794f7a7ebff6 100644
--- a/libcxx/include/limits
+++ b/libcxx/include/limits
@@ -177,6 +177,21 @@ protected:
static _LIBCPP_CONSTEXPR const float_round_style round_style = round_toward_zero;
};
+// Maps an integer type _Tp to its unsigned counterpart. Used by the digits
+// computation below so it can apply __builtin_popcountg uniformly regardless
+// of signedness. __make_unsigned isn't used directly because it rejects
+// already-unsigned types.
+# if __has_builtin(__builtin_popcountg) && __has_builtin(__make_unsigned)
+template <class _Tp, bool _IsSigned = (_Tp(-1) < _Tp(0))>
+struct __numeric_limits_unsigned_type {
+ using type = _Tp;
+};
+template <class _Tp>
+struct __numeric_limits_unsigned_type<_Tp, true> {
+ using type = __make_unsigned(_Tp);
+};
+# endif
+
template <class _Tp>
class __libcpp_numeric_limits<_Tp, true> {
protected:
@@ -184,9 +199,24 @@ protected:
static _LIBCPP_CONSTEXPR const bool is_specialized = true;
- static _LIBCPP_CONSTEXPR const bool is_signed = type(-1) < type(0);
- static _LIBCPP_CONSTEXPR const int digits = static_cast<int>(sizeof(type) * __CHAR_BIT__ - is_signed);
- static _LIBCPP_CONSTEXPR const int digits10 = digits * 3 / 10;
+ static _LIBCPP_CONSTEXPR const bool is_signed = type(-1) < type(0);
+ // For _BitInt(N), sizeof(type) * CHAR_BIT may exceed N due to padding
+ // bits. Count the actual value bits: ~unsigned_type(0) leaves padding
+ // zero, so popcount yields the value-bit width. Standard integer types
+ // have no padding, so the result matches sizeof * CHAR_BIT.
+# if __has_builtin(__builtin_popcountg) && __has_builtin(__make_unsigned)
+ using __unsigned_type = typename __numeric_limits_unsigned_type<type>::type;
+ static _LIBCPP_CONSTEXPR const int digits =
+ __builtin_popcountg(static_cast<__unsigned_type>(~__unsigned_type(0))) - is_signed;
+# else
+ static _LIBCPP_CONSTEXPR const int digits = static_cast<int>(sizeof(type) * __CHAR_BIT__ - is_signed);
+# endif
+ // digits10 = floor(digits * log10(2)). The rational 1936274/6432163 is a
+ // continued-fraction convergent of log10(2) and matches floor(d*log10(2))
+ // exactly for every d in [1, 8388608], i.e. up to __BITINT_MAXWIDTH__ on
+ // x86. The old digits*3/10 was wrong for digits >= 256 (gave 76 instead
+ // of 77); 643/2136 would have broken again at digits=15437.
+ static _LIBCPP_CONSTEXPR const int digits10 = static_cast<int>((digits * 1936274LL) / 6432163);
static _LIBCPP_CONSTEXPR const int max_digits10 = 0;
static _LIBCPP_CONSTEXPR const type __min = is_signed ? _Tp(_Tp(1) << digits) : 0;
static _LIBCPP_CONSTEXPR const type __max = is_signed ? type(type(~0) ^ __min) : type(~0);
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
index 4608a3d01ba14..4e3e3c593afd0 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
@@ -53,5 +53,45 @@ int main(int, char**)
test<double, DBL_MANT_DIG>();
test<long double, LDBL_MANT_DIG>();
- return 0;
+ // _BitInt(N): digits must equal N for unsigned and N-1 for signed,
+ // regardless of padding. The old sizeof*CHAR_BIT form counted padding
+ // bits for non-byte-aligned widths (e.g. unsigned _BitInt(13) reported
+ // 16 instead of 13).
+#if TEST_HAS_EXTENSION(bit_int)
+ // Byte-aligned widths (historically correct).
+ test<unsigned _BitInt(8), 8>();
+ test<signed _BitInt(8), 7>();
+ test<unsigned _BitInt(32), 32>();
+ test<signed _BitInt(32), 31>();
+ test<unsigned _BitInt(64), 64>();
+ test<signed _BitInt(64), 63>();
+
+ // Odd widths: these are the cases the old formula got wrong.
+ test<unsigned _BitInt(7), 7>();
+ test<signed _BitInt(7), 6>();
+ test<unsigned _BitInt(13), 13>();
+ test<signed _BitInt(13), 12>();
+ test<unsigned _BitInt(37), 37>();
+ test<signed _BitInt(37), 36>();
+# if __BITINT_MAXWIDTH__ >= 128
+ test<unsigned _BitInt(77), 77>();
+ test<signed _BitInt(77), 76>();
+ test<unsigned _BitInt(128), 128>();
+ test<signed _BitInt(128), 127>();
+# endif
+# if __BITINT_MAXWIDTH__ >= 256
+ test<unsigned _BitInt(129), 129>();
+ test<signed _BitInt(129), 128>();
+ test<unsigned _BitInt(255), 255>();
+ test<signed _BitInt(255), 254>();
+ test<unsigned _BitInt(256), 256>();
+ test<signed _BitInt(256), 255>();
+# endif
+# if __BITINT_MAXWIDTH__ >= 4096
+ test<unsigned _BitInt(4096), 4096>();
+ test<signed _BitInt(4096), 4095>();
+# endif
+#endif // TEST_HAS_EXTENSION(bit_int)
+
+ return 0;
}
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
index 41f134a706bcd..18a24904b2285 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
@@ -57,5 +57,57 @@ int main(int, char**)
test<double, DBL_DIG>();
test<long double, LDBL_DIG>();
- return 0;
+ // _BitInt(N): digits10 = floor((N - is_signed) * log10(2)). The formula
+ // `digits * 3 / 10` was wrong for digits >= 256: e.g. unsigned
+ // _BitInt(256) has digits=256 and log10(2^256) ~= 77.06, so digits10
+ // must be 77, not 76.
+#if TEST_HAS_EXTENSION(bit_int)
+ test<unsigned _BitInt(8), 2>(); // digits=8, log10=2.4
+ test<signed _BitInt(8), 2>(); // digits=7, log10=2.1
+ test<unsigned _BitInt(13), 3>(); // digits=13, log10=3.9
+ test<signed _BitInt(13), 3>(); // digits=12, log10=3.6
+ test<unsigned _BitInt(32), 9>(); // digits=32, log10=9.6
+ test<unsigned _BitInt(37), 11>(); // digits=37, log10=11.1
+ test<unsigned _BitInt(64), 19>(); // digits=64, log10=19.3
+ test<signed _BitInt(64), 18>(); // digits=63, log10=18.9
+# if __BITINT_MAXWIDTH__ >= 128
+ test<unsigned _BitInt(77), 23>(); // digits=77, log10=23.2
+ test<signed _BitInt(77), 22>(); // digits=76, log10=22.9
+ test<unsigned _BitInt(128), 38>(); // digits=128, log10=38.5
+ test<signed _BitInt(128), 38>(); // digits=127, log10=38.2
+# endif
+# if __BITINT_MAXWIDTH__ >= 256
+ test<unsigned _BitInt(129), 38>(); // digits=129, log10=38.8
+ test<unsigned _BitInt(255), 76>(); // digits=255, log10=76.8
+ test<unsigned _BitInt(256), 77>(); // digits=256, log10=77.1 (old: 76)
+ test<signed _BitInt(256), 76>(); // digits=255, log10=76.8
+ test<unsigned _BitInt(257), 77>(); // digits=257, log10=77.4
+# endif
+# if __BITINT_MAXWIDTH__ >= 4096
+ test<unsigned _BitInt(4096), 1233>(); // digits=4096, log10=1233.0
+ test<signed _BitInt(4096), 1232>(); // digits=4095, log10=1232.7
+# endif
+
+ // Very wide _BitInt: pin the log10(2) approximation used by digits10.
+ // Each width is the first point at which a coarser rational convergent
+ // of log10(2) would give the wrong floor, so these tests bite if the
+ // formula ever regresses.
+# if __BITINT_MAXWIDTH__ >= 15437
+ // 643/2136 (the pre-fix approximation) gives 4646 here; correct is 4647.
+ test<unsigned _BitInt(15437), 4647>();
+# endif
+# if __BITINT_MAXWIDTH__ >= 70777
+ // 8651/28738 gives 21305 here; correct is 21306.
+ test<unsigned _BitInt(70777), 21306>();
+# endif
+# if __BITINT_MAXWIDTH__ >= 1000000
+ test<unsigned _BitInt(1000000), 301029>();
+# endif
+# if __BITINT_MAXWIDTH__ >= 8388608
+ // Pin the exact upper bound of the approximation.
+ test<unsigned _BitInt(8388608), 2525222>();
+# endif
+#endif // TEST_HAS_EXTENSION(bit_int)
+
+ return 0;
}
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/max.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/max.pass.cpp
index 2f7c1f899a73f..fe8f039416d3a 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/max.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/max.pass.cpp
@@ -65,5 +65,26 @@ int main(int, char**)
test<double>(DBL_MAX);
test<long double>(LDBL_MAX);
- return 0;
+ // _BitInt(N): max is 2^N - 1 for unsigned and 2^(N-1) - 1 for signed.
+ // Exercises the digits fix through `__max = ~0 ^ __min`.
+#if TEST_HAS_EXTENSION(bit_int)
+ test<unsigned _BitInt(8)>((unsigned _BitInt(8)) ~(unsigned _BitInt(8))0);
+ test<signed _BitInt(8)>((signed _BitInt(8))0x7F);
+ test<unsigned _BitInt(13)>((unsigned _BitInt(13))0x1FFF);
+ test<signed _BitInt(13)>((signed _BitInt(13))0x0FFF);
+ test<unsigned _BitInt(64)>((unsigned _BitInt(64)) ~(unsigned _BitInt(64))0);
+ test<signed _BitInt(64)>((signed _BitInt(64))0x7FFFFFFFFFFFFFFFLL);
+# if __BITINT_MAXWIDTH__ >= 128
+ test<unsigned _BitInt(77)>((unsigned _BitInt(77)) ~(unsigned _BitInt(77))0);
+ test<signed _BitInt(77)>((signed _BitInt(77)) ~((signed _BitInt(77))1 << 76));
+ test<unsigned _BitInt(128)>((unsigned _BitInt(128)) ~(unsigned _BitInt(128))0);
+ test<signed _BitInt(128)>((signed _BitInt(128)) ~((signed _BitInt(128))1 << 127));
+# endif
+# if __BITINT_MAXWIDTH__ >= 256
+ test<unsigned _BitInt(256)>((unsigned _BitInt(256)) ~(unsigned _BitInt(256))0);
+ test<signed _BitInt(256)>((signed _BitInt(256)) ~((signed _BitInt(256))1 << 255));
+# endif
+#endif
+
+ return 0;
}
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/min.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/min.pass.cpp
index 09877362447bc..a9c72da2103b4 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/min.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/min.pass.cpp
@@ -65,5 +65,27 @@ int main(int, char**)
test<double>(DBL_MIN);
test<long double>(LDBL_MIN);
- return 0;
+ // _BitInt(N): min is 0 for unsigned and -2^(N-1) for signed. The shift
+ // `1 << digits` flowed through the buggy digits field, so this also
+ // exercises the digits fix for non-byte-aligned widths.
+#if TEST_HAS_EXTENSION(bit_int)
+ test<unsigned _BitInt(8)>(0);
+ test<signed _BitInt(8)>(-(signed _BitInt(8))(1 << 7));
+ test<unsigned _BitInt(13)>(0);
+ test<signed _BitInt(13)>(-(signed _BitInt(13))(1 << 12));
+ test<unsigned _BitInt(64)>(0);
+ test<signed _BitInt(64)>(-(signed _BitInt(64))(1ULL << 63));
+# if __BITINT_MAXWIDTH__ >= 128
+ test<unsigned _BitInt(77)>(0);
+ test<signed _BitInt(77)>(-((signed _BitInt(77))1 << 76));
+ test<unsigned _BitInt(128)>(0);
+ test<signed _BitInt(128)>(-((signed _BitInt(128))1 << 127));
+# endif
+# if __BITINT_MAXWIDTH__ >= 256
+ test<unsigned _BitInt(256)>(0);
+ test<signed _BitInt(256)>(-((signed _BitInt(256))1 << 255));
+# endif
+#endif
+
+ return 0;
}
>From b20619b45450a29f48cfb4aaa4d6cea3cc1bd724 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Mon, 20 Apr 2026 18:49:02 +0200
Subject: [PATCH 2/4] [libc++][NFC] Simplify numeric_limits digits helper
Use __make_unsigned_t<type> directly in the digits computation instead
of a local bool-parameterised helper. __make_unsigned_t already covers
unsigned types as an identity, and libc++'s alias has a portable
fallback, so there is no need to guard on __has_builtin(__make_unsigned)
separately.
Addresses review comments on #193002.
Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
libcxx/include/limits | 20 +++-----------------
1 file changed, 3 insertions(+), 17 deletions(-)
diff --git a/libcxx/include/limits b/libcxx/include/limits
index 1794f7a7ebff6..e772aecca99d7 100644
--- a/libcxx/include/limits
+++ b/libcxx/include/limits
@@ -108,6 +108,7 @@ template<> class numeric_limits<cv long double>;
# include <__config>
# include <__type_traits/is_arithmetic.h>
# include <__type_traits/is_same.h>
+# include <__type_traits/make_unsigned.h>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -177,21 +178,6 @@ protected:
static _LIBCPP_CONSTEXPR const float_round_style round_style = round_toward_zero;
};
-// Maps an integer type _Tp to its unsigned counterpart. Used by the digits
-// computation below so it can apply __builtin_popcountg uniformly regardless
-// of signedness. __make_unsigned isn't used directly because it rejects
-// already-unsigned types.
-# if __has_builtin(__builtin_popcountg) && __has_builtin(__make_unsigned)
-template <class _Tp, bool _IsSigned = (_Tp(-1) < _Tp(0))>
-struct __numeric_limits_unsigned_type {
- using type = _Tp;
-};
-template <class _Tp>
-struct __numeric_limits_unsigned_type<_Tp, true> {
- using type = __make_unsigned(_Tp);
-};
-# endif
-
template <class _Tp>
class __libcpp_numeric_limits<_Tp, true> {
protected:
@@ -204,8 +190,8 @@ protected:
// bits. Count the actual value bits: ~unsigned_type(0) leaves padding
// zero, so popcount yields the value-bit width. Standard integer types
// have no padding, so the result matches sizeof * CHAR_BIT.
-# if __has_builtin(__builtin_popcountg) && __has_builtin(__make_unsigned)
- using __unsigned_type = typename __numeric_limits_unsigned_type<type>::type;
+# if __has_builtin(__builtin_popcountg)
+ using __unsigned_type = __make_unsigned_t<type>;
static _LIBCPP_CONSTEXPR const int digits =
__builtin_popcountg(static_cast<__unsigned_type>(~__unsigned_type(0))) - is_signed;
# else
>From 72743aa0151030508e6db50b5e791c91f8d0b426 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Mon, 20 Apr 2026 18:49:57 +0200
Subject: [PATCH 3/4] [libc++][NFC] Drop historical remarks from numeric_limits
comments
Comments that described what the pre-fix code did wrong ("old digits*3/10
was wrong", "the old formula got wrong", "(historically correct)", etc.)
belong in the commit log, not the source. Remove them and reword the
wide-_BitInt test comments to describe the regression guard in
forward-looking terms ("a coarser convergent would give..." instead of
"the pre-fix approximation gave...").
Addresses review feedback on #193002.
Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
libcxx/include/limits | 8 +++-----
.../limits/numeric.limits.members/digits.pass.cpp | 8 +++-----
.../limits/numeric.limits.members/digits10.pass.cpp | 11 ++++-------
3 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/libcxx/include/limits b/libcxx/include/limits
index e772aecca99d7..43bfd83d4e2b0 100644
--- a/libcxx/include/limits
+++ b/libcxx/include/limits
@@ -197,11 +197,9 @@ protected:
# else
static _LIBCPP_CONSTEXPR const int digits = static_cast<int>(sizeof(type) * __CHAR_BIT__ - is_signed);
# endif
- // digits10 = floor(digits * log10(2)). The rational 1936274/6432163 is a
- // continued-fraction convergent of log10(2) and matches floor(d*log10(2))
- // exactly for every d in [1, 8388608], i.e. up to __BITINT_MAXWIDTH__ on
- // x86. The old digits*3/10 was wrong for digits >= 256 (gave 76 instead
- // of 77); 643/2136 would have broken again at digits=15437.
+ // digits10 = floor(digits * log10(2)). 1936274/6432163 is a continued-fraction
+ // convergent of log10(2) and matches floor(d * log10(2)) exactly for every d
+ // in [1, 8388608], i.e. up to __BITINT_MAXWIDTH__ on x86.
static _LIBCPP_CONSTEXPR const int digits10 = static_cast<int>((digits * 1936274LL) / 6432163);
static _LIBCPP_CONSTEXPR const int max_digits10 = 0;
static _LIBCPP_CONSTEXPR const type __min = is_signed ? _Tp(_Tp(1) << digits) : 0;
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
index 4e3e3c593afd0..807ea69f07680 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits.pass.cpp
@@ -54,11 +54,9 @@ int main(int, char**)
test<long double, LDBL_MANT_DIG>();
// _BitInt(N): digits must equal N for unsigned and N-1 for signed,
- // regardless of padding. The old sizeof*CHAR_BIT form counted padding
- // bits for non-byte-aligned widths (e.g. unsigned _BitInt(13) reported
- // 16 instead of 13).
+ // regardless of padding bits for non-byte-aligned widths.
#if TEST_HAS_EXTENSION(bit_int)
- // Byte-aligned widths (historically correct).
+ // Byte-aligned widths.
test<unsigned _BitInt(8), 8>();
test<signed _BitInt(8), 7>();
test<unsigned _BitInt(32), 32>();
@@ -66,7 +64,7 @@ int main(int, char**)
test<unsigned _BitInt(64), 64>();
test<signed _BitInt(64), 63>();
- // Odd widths: these are the cases the old formula got wrong.
+ // Non-byte-aligned widths.
test<unsigned _BitInt(7), 7>();
test<signed _BitInt(7), 6>();
test<unsigned _BitInt(13), 13>();
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
index 18a24904b2285..357a9d6837dd1 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
@@ -57,10 +57,7 @@ int main(int, char**)
test<double, DBL_DIG>();
test<long double, LDBL_DIG>();
- // _BitInt(N): digits10 = floor((N - is_signed) * log10(2)). The formula
- // `digits * 3 / 10` was wrong for digits >= 256: e.g. unsigned
- // _BitInt(256) has digits=256 and log10(2^256) ~= 77.06, so digits10
- // must be 77, not 76.
+ // _BitInt(N): digits10 = floor((N - is_signed) * log10(2)).
#if TEST_HAS_EXTENSION(bit_int)
test<unsigned _BitInt(8), 2>(); // digits=8, log10=2.4
test<signed _BitInt(8), 2>(); // digits=7, log10=2.1
@@ -79,7 +76,7 @@ int main(int, char**)
# if __BITINT_MAXWIDTH__ >= 256
test<unsigned _BitInt(129), 38>(); // digits=129, log10=38.8
test<unsigned _BitInt(255), 76>(); // digits=255, log10=76.8
- test<unsigned _BitInt(256), 77>(); // digits=256, log10=77.1 (old: 76)
+ test<unsigned _BitInt(256), 77>(); // digits=256, log10=77.1
test<signed _BitInt(256), 76>(); // digits=255, log10=76.8
test<unsigned _BitInt(257), 77>(); // digits=257, log10=77.4
# endif
@@ -93,11 +90,11 @@ int main(int, char**)
// of log10(2) would give the wrong floor, so these tests bite if the
// formula ever regresses.
# if __BITINT_MAXWIDTH__ >= 15437
- // 643/2136 (the pre-fix approximation) gives 4646 here; correct is 4647.
+ // A coarser convergent (643/2136) would give 4646 here; correct is 4647.
test<unsigned _BitInt(15437), 4647>();
# endif
# if __BITINT_MAXWIDTH__ >= 70777
- // 8651/28738 gives 21305 here; correct is 21306.
+ // A coarser convergent (8651/28738) would give 21305 here; correct is 21306.
test<unsigned _BitInt(70777), 21306>();
# endif
# if __BITINT_MAXWIDTH__ >= 1000000
>From fdef69e2927fc8c249b709de01c647272d7ab28e Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Mon, 20 Apr 2026 18:50:22 +0200
Subject: [PATCH 4/4] [libc++] Pin __BITINT_MAXWIDTH__ bound for digits10 test
Add a LIBCPP_STATIC_ASSERT that fires if __BITINT_MAXWIDTH__ exceeds
8388608, the largest width for which the 1936274/6432163 approximation
has been verified. The formula stays exact up to d=51132156, so bumping
the assertion is a matter of extending the coverage rather than
redesigning the rational.
Addresses review feedback on #193002.
Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
.../limits/numeric.limits.members/digits10.pass.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
index 357a9d6837dd1..002f951b2b829 100644
--- a/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/limits/numeric.limits.members/digits10.pass.cpp
@@ -104,6 +104,10 @@ int main(int, char**)
// Pin the exact upper bound of the approximation.
test<unsigned _BitInt(8388608), 2525222>();
# endif
+ // The 1936274/6432163 convergent stays exact up to d=51132156. 8388608 is
+ // the largest width tested above, so if Clang raises __BITINT_MAXWIDTH__,
+ // extend the coverage before trusting the formula at the new range.
+ LIBCPP_STATIC_ASSERT(__BITINT_MAXWIDTH__ <= 8388608);
#endif // TEST_HAS_EXTENSION(bit_int)
return 0;
More information about the libcxx-commits
mailing list