---
title: "Slides for P3666R4 Bit-precise integers"
document: P4157R1
date: 2026-06-11
audience: LEWG
reply-to:
  - "Jan Schultke <janschultke@gmail.com>"
---

# Bit-precise integers P3666R4

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 1

## Introduction

C23 now has `_BitInt` type for N-bit integers (WG14 N2763, N2775):

```cpp
// 8-bit unsigned integer initialized with value 255.
// The literal suffix wb is unnecessary in this case.
unsigned _BitInt(8) x = 0xFFwb;
```

- implemented by GCC and Clang; max: `_BitInt(8'388'608)`
- forwarded by EWG to LEWG/CWG for C++29

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 2

## P3666R3 LEWG in Croydon 2026

> POLL
> 
> : We should prevent library support for
> 
> _BitInt
> 
> | SF | F | N | A | SA |
> | --- | --- | --- | --- | --- |
> | 5 | 9 | 2 | 3 | 1 |
> 
> Outcome
> 
> : Consensus in favor
> 
> POLL
> 
> :
> 
> std::is_integral_v<_BitInt(N)>
> 
> should be false
> 
> | SF | F | N | A | SA |
> | --- | --- | --- | --- | --- |
> | 7 | 4 | 5 | 2 | 0 |
> 
> Outcome
> 
> : Consensus in favor
> 
> POLL
> 
> : We should provide alias templates for
> 
> _BitInt
> 
> […]
> 
> | SF | F | N | A | SA |
> | --- | --- | --- | --- | --- |
> | 0 | 3 | 8 | 3 | 5 |
> 
> Outcome
> 
> : Consensus against
> 
> POLL
> 
> : We should provide
> 
> std::bit_int
> 
> and
> 
> std::bit_uint
> 
> as class templates […]
> 
> | SF | F | N | A | SA |
> | --- | --- | --- | --- | --- |
> | 2 | 4 | 8 | 4 | 0 |
> 
> Outcome
> 
> : No consensus

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 3

## std::is_integral_v<_BitInt(N)>

> **POLL**: `std::is_integral_v<_BitInt(N)>` should be false
> 
> | SF | F | N | A | SA |
> | --- | --- | --- | --- | --- |
> | 7 | 4 | 5 | 2 | 0 |
> 
> **Outcome**: Consensus in favor

- other poll outcomes are implemented, *but not this one*
- LEWG voted with incomplete information
- here is why …

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 4

## Intuition

> A bit-precise integer is an integer.

- LEWG disagrees
- integer type is an alias for integral type ([[basic.fundamental]](https://eel.is/c++draft/basic.fundamental))
- no clear way to avoid obvious teachability problems

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 5

## Procedural issues

### Two schools of thought

1. The type trait simply reports what is an integer type in the core language.
  - LEWG has no design freedom; EWG decides
  - poll outcome is procedurally invalid
2. The type trait is observable behavior, anything else is wording strategy.
  - LEWG gets to decide
  - integer type can be redefined if use sites are updated
  - e.g. `std::is_class_v` is false for unions (but unions are class types)

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 6

## P3666R4 type taxonomy — C compatibility

```
int
```

- P3666R4 mostly matches C2y taxonomy
- e.g. character types are different
- two *radically* different taxonomies are harder to learn

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 7

## std::is_integral is underconstraining

- rationale for poll: existing `std::is_integral` constraints would change
- surprisingly, `std::is_integral_v` also matches:
  - `char`
  - `char32_t`
  - `const volatile bool`
  - […]
- users likely expect `int`, `unsigned long`, etc. instead
- `std::is_integral_v` by itself is not a suitable constraint anyway

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 8

## std::is_integral_v<_BitInt(N)> is already true in libc++

- (but false in libstdc++)
- this was *not* mentioned in LEWG
- *total* reframing of the poll:
  - <del>What should the behavior be in the future?</del>
  - <ins>Do we want to change the existing libc++ behavior?</ins>
- if LLVM is fine with shipping this behavior, why are we not?
- do we know better?

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 9

## Most code handles the change fine

```cpp
template<std::integral T>
T mul(T x, T y) {
return x * y;
}
```

- no innate problem with `T = _BitInt(N)`
- edge case: promotion to `int` can prevent some UB
- … but also introduce UB (e.g. `T = unsigned short`)
- problems usually arise only for *huge* widths

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 10

## Extended integer types

- integral type has always been arbitrarily extendable
- implementation can add `_ExtInt(N)` as extended integer type with same properties as `_BitInt(N)`
- `std::is_integral_v<_ExtInt(N)>` is `true` then

### Key question:

> If the implementation can arbitrarily extend integral type, why can't we extend it with `_BitInt`?

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 11

## std::is_integral_v<_BitInt(N)> is eternal

### Key question:

> If `std::is_integral_v<_BitInt(N)>` is false now, can we suddenly make it `true` later?

### Likely answer:

This is our *only* chance to make it true.

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 12

## Extending categories of fundamental types

### Past:

- character type: `char`, `wchar_t`, <ins>`char8_t`, `char16_t`, `char32_t`</ins>
- floating-point type: `float`, `double`, `long double`, <ins>`std::bfloat16_t`, […], `std::float128_t`</ins>

### Future:

- integral type: `int`, `unsigned long`, […] <ins>`_BitInt(N)`</ins>
- floating-point type: `float`, […], `std::float128_t`, <ins>`std::decimal32_t`, `std::decimal64_t`, `std::decimal128_t`</ins>

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 13

## The end

- Let's get `std::is_integral_v<_BitInt(N)>` right!
- Most of remaining of design/wording is obvious consequence.

Jan Schultke
 | 
LEWG Slides for P3666R4
—
Bit-precise integers
 | 
Brno 2026
 | 
Slide 14
