One more datapoint, I hope I am not causing too much noise.
I extracted all recent _BitInt std support PRs and had a candid look at the numbers. I crunched the stats with automated scripts and AI-assisted collected datapoint, which I checked.
The Tl;Dr is at th every end. Feedback welcome.
What libc++ provides for _BitInt today
A factual snapshot for the P3666R4 discussion and the LEWG Rio review.
Disclosure: the author favors making _BitInt a first-class library type and uses it in production. This document reports what libc++ ships, what it cost to build, and what is left. It is a datapoint, not a position. The cost figures are checked against two independent sources, the merged Git commits and GitHub’s own line counts, which agree exactly.
Background in one paragraph
_BitInt(N) is an integer of arbitrary, fixed width, for example _BitInt(256). It exists in C23 and in Clang today. P3666R4 proposes to standardize it for C++29, but its section 5 would keep it out of most of the standard library by constraining facilities to reject it. libc++ already supports _BitInt in several of those facilities as an extension. This surveys that existing support, so the discussion can start from what exists rather than from estimates.
What works in libc++ today
A meaningful part of _BitInt usability predates the recent work. The compiler and baseline libc++ already provided it, at no cost to the three PRs below. Separating the two matters for reading the cost figures: the PRs bought the second table, not the first.
Predated the recent work (compiler builtins and baseline libc++):
| Facility |
_BitInt support |
Why it already worked |
is_integral, is_signed, is_unsigned, make_signed/make_unsigned |
All widths |
Compiler builtins; same path as __int128 |
| Arithmetic, comparison, and conversion operators |
All widths |
Language feature |
numeric_limits: is_specialized, min, max |
All widths |
Already specialized |
to_chars / from_chars |
Up to 128 bits |
Gated on public is_integral, already true |
byteswap acceptance |
Up to 128 bits, byte-aligned |
Gated on std::integral, already true |
Added or fixed by the three PRs:
| Facility |
Change |
Source |
<bit>: popcount, countl_zero/one, countr_zero/one, bit_width, bit_ceil, bit_floor, rotl, rotr, has_single_bit |
Enabled (were constrained out) |
#185027 trait cascade |
cmp_equal/cmp_less/… and in_range |
Enabled |
#185027 trait cascade |
Saturation arithmetic (add_sat, sub_sat, …) |
Enabled |
#185027 trait cascade |
mdspan extents (as the index type) |
Enabled |
#185027 trait cascade |
format |
Enabled, up to 128 bits |
#185027 trait cascade |
numeric_limits: digits, digits10 |
Fixed for non-byte-aligned widths |
#193002 |
byteswap |
Added padding-bit rejection (correctness) |
#196512 |
What it cost to build
Three merged pull requests deliver everything above.
| PR |
Delivered |
Implementation |
Tests |
| #185027 |
the trait cascade, reaching 5 facility areas |
24 lines, 1 file |
1327 lines, 15 files |
| #193002 |
the numeric_limits fix |
11 lines, 1 file |
138 lines, 4 files |
| #196512 |
byteswap |
24 lines, 1 file |
209 lines, 4 files |
| Total |
about 8 facility areas |
59 lines, 3 files |
1674 lines |
Three things stand out. The implementation is small: 59 lines across three header files, each a constraint or trait adjustment rather than new machinery. The cost is almost entirely tests, about 96 percent of the changed lines. And a single 24-line trait change reached five facilities at once with no per-facility implementation. The tests cover representative widths such as 8, 13, 48, 64, 128, and 256, signed and unsigned, rather than every possible N.
One clarification on what these numbers do and do not capture. They are the final merged code, not the effort. The elapsed time and the number of review rounds ran higher than the work itself, because the author was new to libc++. A good share of the review went to local conventions rather than to _BitInt: where tests belong, formatting, and a feature probe that silently disabled some tests until a reviewer caught it. An experienced libc++ contributor would reach the same result with less back-and-forth. The intrinsic cost is the code above; the rest was onboarding.
What is not done yet
Needs tests only, because the trait cascade already makes the code work: gcd, lcm, midpoint, and std::abs (a pull request is open). Roughly one test file each.
Needs genuinely new code: to_chars, from_chars, and format for widths above 128 bits. These need arbitrary-precision decimal conversion, which the library does not have today. The work is real but bounded, and one decimal core serves all three.
Deferred on purpose: std::hash. Its output becomes a permanent compatibility contract once shipped, so it should wait for more experience.
Out of scope here: std::atomic and std::simd, which are separate and larger efforts, and _BitInt wider than 128 bits on non-x86 targets, which is a compiler back-end limit rather than a library one.
Honest caveats
- Tests do dominate the cost. The concern that supporting
_BitInt enlarges the test surface is correct. The qualifier is that the per-facility cost is small and bounded, and one trait change cascades to many facilities.
- Supporting a facility is an ongoing maintenance commitment, not a one-time cost.
- Wide-value
charconv and format is genuine new work, and it is x86-only today.
- Clang documents the
_BitInt ABI as not yet stabilized. That is a reason for caution that is independent of library cost.
- This is one implementation and largely one contributor. libstdc++ and the MSVC library may see different costs.
Bottom line
Most of first-class _BitInt library support already exists in libc++, and it was cheap to build, because one trait change cascades and the rest is tests. The single piece that needs real new code is arbitrary-precision decimal conversion for values wider than 128 bits, and it is bounded. The remaining items are deferred by choice or out of scope. Whatever the committee decides, this is the ground truth of what shipping it actually took.