std :: big_int
- Document number:
- D4444R0
- Date:
2026-04-16 - Audience:
- SG6
- Project:
- ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
- Reply-to:
- Jan Schultke <janschultke@gmail.com>
- GitHub Issue:
- wg21.link/P4444/github
- Source:
- github.com/eisenwave/cpp-proposals/blob/master/src/big-int.cow
.
Other sections will be filled in over time.
Contents
Design
Wording
[version.syn]
[big.int]
[charconv]
[numeric.int.div]
[numeric.abs]
[numeric.sat.cast]
[numeric.conversions]
References
1. Design
- What string conversions to offer?
- Where to store size and capacity of the allocation? Within the container, or in a header in front of the allocated limbs?
-
Be clever about the return type of
?operator % -
Provide constructors taking
despite having user-defined literals?std :: string_view -
Provide
andoperator == support for floating-point? If so, handle NaN silently?operator <=>
2. Wording
The changes are relative to [N5032].
[version.syn]
Add a feature-test macro to [version.syn] as follows:
[big.int]
In Clause [numerics], insert a new subclause immediately following [complex.numbers].
X Arbitrary-precision arithmetic [big.int]
X.1 General [big.int.general]
1 The header
defines a class template for performing arbitrary-precision integer arithmetic, as well as related type aliases.<big_int> X.2 Header
synopsis [big.int.syn]<big_int> #include <compare> #include <span> namespace std { // alias uint_multiprecision_t using uint_multiprecision_t = see below ; // [big.int.class], class template basic_big_int template < size_t min_inplace_capacity , class Allocator = allocator < uint_multiprecision_t >> class basic_big_int ; // [big.int.expos], exposition-only helpers template < class T > concept signed-or-unsigned = see below ; // exposition only template < class T > concept arbitrary-integer = see below ; // exposition only template < class T > concept arbitrary-arithmetic-type = see below ; // exposition only template < class L , class R > using common-big-int-type = see below ; // exposition only template < class T , class U > concept common-big-int-type-with = requires { // exposition only typename common-big-int-type < T , U > ; } ; // [big.int.alias], alias big_int using big_int = basic_big_int < see below > ; // [big.int.cmp], non-member comparison operator functions template < class L , common-big-int-type-with < L > R > constexpr bool operator == ( const L & lhs , const R & rhs ) noexcept ; template < class L , common-big-int-type-with < L > R > constexpr strong_ordering operator <=> ( const L & lhs , const R & rhs ) noexcept ; // [big.int.binary], binary operations template < class L , class R > constexpr common-big-int-type < L , R > operator + ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator - ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator * ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator / ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator % ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator & ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator | ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator ^ ( L && x , R && y ) ; template < class T , signed-or-unsigned S > remove_cvref_t < T > operator << ( T && x , S s ) ; template < class T , signed-or-unsigned S > remove_cvref_t < T > operator >> ( T && x , S s ) ; namespace pmr { template < size_t b > using basic_big_int = std :: basic_big_int < b , polymorphic_allocator < uint_multiprecision_t >> ; using big_int = basic_big_int < std :: big_int :: inplace_bits > ; } // [big.int.hash], hash support template < class T > struct hash ; template < size_t b , class A > struct hash < basic_big_int < b , A >> ; // [big.int.literal], literals inline namespace literals { inline namespace big_int_literals { template < char ... digits > constexpr big_int operator " " n ( ) noexcept ( see below ) ; } } } 1 The type alias
denotes a standard unsigned or extended unsigned integer type ([basic.fundamental]) which has no padding bits.uint_multiprecision_t 2 Recommended practice:
should be chosen to have the greatest possible width so that an arithmetic expression ([expr.pre]) performed on operands of the type corresponds to single instruction in the execution environment.uint_multiprecision_t
It is important not to overconstrain here because there are many edge cases in architectures:
- 8-bit microcontrollers only have 8-bit arithmetic but can address memory with 16-bit addressing; the appropriate type there is presumably
despiteuint8_t andsize_t being wider.int - WASM32 only has 32-bit addressable memory and has a 32-bit
; however, WASM32 supports 64-bit arithmetic instructions, so the appropriate limb type there issize_t .uint64_t - In most cases, the limb type can simply be
.size_t X.3 Class template
[big.int.class]basic_big_int template < size_t min_inplace_capacity , class Allocator > class basic_big_int { // [big.int.defns], types and constants using allocator_type = Allocator ; using size_type = implementation-defined ; static constexpr size_type inplace_representation_capacity = see below ; static constexpr size_type inplace_capacity = inplace_representation_capacity * numeric_limits < uint_multiprecision_t > :: digits ; // [big.int.expos], exposition-only helpers template < class T > inline constexpr bool no-alloc-constructible-from = see below ; // exposition only // [big.int.cons], construct/copy/destroy constexpr basic_big_int ( ) noexcept ( noexcept ( Allocator ( ) ) ) ; constexpr explicit basic_big_int ( const Allocator & a ) noexcept ; constexpr basic_big_int ( const basic_big_int & x ) ; constexpr basic_big_int ( basic_big_int && x ) noexcept ; template < arbitrary-arithmetic-type T > constexpr explicit ( see below ) basic_big_int ( T && x ) noexcept ( no-alloc-constructible-from < T > ) ; template < arbitrary-arithmetic-type T > constexpr explicit basic_big_int ( const T & x , const Allocator & a ) noexcept ( no-alloc-constructible-from < T > ) ; template < input_range R > requires signed-or-unsigned < ranges :: range_value_t < R >> constexpr explicit basic_big_int ( from_range_t , R && , const Allocator & a = Allocator ( ) ) ; constexpr ~ basic_big_int ( ) ; // [big.int.ops], operations constexpr span < const uint_multiprecision_t > representation ( ) const noexcept ; constexpr size_type size ( ) const noexcept ; constexpr size_type representation_size ( ) const noexcept ; constexpr size_type max_size ( ) const noexcept ; constexpr size_type max_representation_size ( ) const noexcept ; constexpr size_type capacity ( ) const noexcept ; constexpr size_type representation_capacity ( ) const noexcept ; constexpr allocator_type get_allocator ( ) const noexcept ; constexpr void reserve ( size_type n ) ; constexpr void reserve_representation ( size_type n ) ; constexpr void shrink_to_fit ( ) ; // [big.int.modifiers], modifiers constexpr basic_big_int & operator = ( const basic_big_int & x ) ; constexpr basic_big_int & operator = ( basic_big_int && x ) noexcept ; template < arbitrary-integer T > constexpr basic_big_int & operator = ( T && x ) noexcept ( no-alloc-constructible-from < T > ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator += ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator -= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator *= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator /= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator %= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator &= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator |= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator ^= ( T && x ) ; template < signed-or-unsigned S > constexpr basic_big_int & operator <<= ( S s ) ; template < signed-or-unsigned S > constexpr basic_big_int & operator >>= ( S s ) ; constexpr void swap ( basic_big_int & x ) noexcept ( allocator_traits < Allocator > :: propagate_on_container_swap :: value || allocator_traits < Allocator > :: is_always_equal :: value ) ; // [big.int.conv], conversions template < class T > constexpr explicit operator T ( ) const noexcept ; // [big.int.unary], unary operations constexpr basic_big_int operator + ( ) const & ; constexpr basic_big_int operator + ( ) && noexcept ; constexpr basic_big_int operator - ( ) const & ; constexpr basic_big_int operator - ( ) && noexcept ; constexpr basic_big_int operator ~ ( ) const & ; constexpr basic_big_int operator ~ ( ) && ; constexpr basic_big_int & operator ++ ( ) & ; constexpr basic_big_int operator ++ ( int ) & ; constexpr basic_big_int & operator -- ( ) & ; constexpr basic_big_int operator -- ( int ) & ; } ; 1 A
represents an integer value; the integer value of an object of integral type is the value ([basic.types.general]) of that object. The magnitude of the integer value of abasic_big_int is either represented using subobjects nested within abasic_big_int or represented within storage obtained from the givenbasic_big_int ; the sign of the integer value is represented separately.Allocator 2 The effective width of an integer value is the width of the smallest hypothetical unsigned integer type ([basic.fundamental]) able to represent the magnitude of .
3 Template parameter
specifies the minimum width of integers that amin_inplace_capacity shall be capable of representing using a subobject nested within.basic_big_int shall be nonzero and less than or equal to an implementation-defined limit. That limit shall be greater than or equal to the maximum width of any standard or extended integer type ([basic.fundamental]).min_inplace_capacity
The design aspiration is that is capable of storing any standard or extended integer type without the use of allocations, includingbasic_big_int and__int128 . This is easily possible by just putting the proper integer type into aunsigned __int128 with the pointer to allocated data.union It is theoretically possible to use a greater limit and store a
within theuint_multiprecision_t [ ] , but this is more difficult to implement than if you only need small object optimizations for a single scalar, so the initial proposal maybe shouldn't mandate it.basic_big_int 4 During constant evaluation, if the effective width of the integer value of a
is less than or equal to itsbasic_big_int member, the object shall not hold an allocation following any operation.inplace_bits
[Note: The behavior is as ifwas called following every operation that may allocate. — end note]shrink_to_fit ( )
This ensures that even without non-transient allocations, as long as the value can be stored directly in the object, you can create a variable. Otherwise, it would be possible to hold e.g. the valueconstexpr big_int in dynamic storage, even though it can be represented without allocations.123 Giving the implementation the freedom to hold an
unnecessary allocationstill makes sense because that allocation may be reused at a later point.5 The representation of a
object is the sequence ofbasic_big_int elements that collectively represents the integer value of that object. Unless otherwise stated,uint_multiprecision_t
- any member function or member function template without
qualifier, andconst - any free function or specialization of a function template that has a parameter of type
rvalue reference tobasic_big_int described in [big.int] invalidates the representation of the object, meaning that results previously returned by
are no longer valid.representation ( ) X.3.1 Types and constants [big.int.defns]
static constexpr size_type inplace_representation_capacity = see below ; 1 The value of the static data member
is the amount ofinplace_representation_capacity nested within auint_multiprecision_t object and which participate in representing its integer value.basic_big_int 2 Remarks: The value of
shall be at leastinplace_representation_capacity .div_to_pos_inf ( min_inplace_capacity , numeric_limits < uint_multiprecision_t > :: digits ) X.3.2 Exposition-only helpers [big.int.expos]
template < class T > concept signed-or-unsigned = see below ; 1 The exposition-only concept
is satisfied and modeled if and only ifsigned-or-unsigned is a signed or unsigned integer type ([basic.fundamental]).T template < class T > concept arbitrary-integer = see below ; 2 The exposition-only concept
is satisfied and modeled if and only ifarbitrary-integer is either a signed or unsigned integer type ([basic.fundamental]) or a specialization ofremove_cvref_t < T > .basic_big_int template < class T > concept arbitrary-arithmetic-type = see below ; 3 The exposition-only concept
is satisfied and modeled if and only ifarbitrary-arithmetic-type is either a cv-unqualified arithmetic type ([basic.fundamental]) or a specialization ofremove_cvref_t < T > .basic_big_int template < class L , class R > using common-big-int-type = see below ; 4 Let
beLT , and letremove_cvref_t < L > beRT .remove_cvref_t < R > 5 Result:
- If
andLT are the same specialization ofRT ,basic_big_int ;LT - otherwise, if
is a specialization ofLT andbasic_big_int is a signed or unsigned integer type ([basic.fundamental]),RT ;LT - otherwise, if
is a specialization ofRT andbasic_big_int is a signed or unsigned integer type ([basic.fundamental]),LT ;RT - otherwise, the type alias is ill-formed.
template < class T > inline constexpr bool no-alloc-constructible-from = see below ; 6 Effects:
isno-alloc-constructible-from iftrue is a signed or unsigned integer type whose width is less than or equal toremove_cvref_t < T > , andinplace_bits otherwise.false X.3.3 Construct/copy/destroy [big.int.cons]
constexpr basic_big_int ( ) noexcept ( noexcept ( Allocator ( ) ) ) ; 1 Effects: Initializes the integer value to zero.
constexpr explicit basic_big_int ( const Allocator & a ) noexcept ; 2 Effects: Initializes the integer value to zero. Initializes the allocator to
.a constexpr basic_big_int ( const basic_big_int & x ) ; 3 Effects: Initializes the integer value to that of
. Initializes the allocator tox .x . get_allocator ( ) 4 Throws: Nothing if the effective width of the integer value of
is less than or equal tox ; otherwise, exceptions thrown during allocation.inplace_bits constexpr basic_big_int ( basic_big_int && x ) noexcept ; 5 Effects: Initializes the integer value to that of
. Initializes the allocator tox .x . get_allocator ( ) template < arbitrary-arithmetic-type T > constexpr explicit ( see below ) basic_big_int ( T && x ) noexcept ( no-alloc-constructible-from < T > ) ; 6 Constraints:
isis_same_v < basic_big_int , remove_cvref_t < T >> .false
This constraint ensures that there is no ambiguity with the copy constructor or move constructor. Also note that we support construction from with other allocators.basic_big_int 7 Preconditions: If
is a floating-point type, the value ofremove_cvref_t < T > is finite.x 8 Effects: If
is an integral type or a specialization ofremove_cvref_t < T > , initializes the integer value to that ofbasic_big_int . Otherwise,x is a floating-point type, and this object is initialized to the integer value obtained by discarding the fractional part ofremove_cvref_t < T > .x 9 Throws: Nothing if the effective width of the integer value this object is initialized with is less than or equal to
; otherwise, exceptions thrown during allocation.inplace_bits 10 Remarks: The constructor is explicit if
is neither a signed or unsigned integer type ([basic.fundamental]) norremove_cvref_t < T > .basic_big_int < inplace_bits , Allocator >
The design goal here is to permit conversion from any arithmetic type as well as for specializations with other allocators, but to make allocator mixing and floating-point conversions explicit. Also explicit is the conversion from character types tobasic_big_int , which is arguably needed because character types and integers are used in different domains.basic_big_int template < arbitrary-arithmetic-type T > constexpr basic_big_int ( const T & x , const Allocator & a ) noexcept ( no-alloc-constructible-from < T > ) ; 11 Preconditions: If
is a floating-point type, the value ofremove_cvref_t < T > is finite.x 12 Effects: If
is an integral type or a specialization ofremove_cvref_t < T > , initializes the integer value to that ofbasic_big_int . Otherwise,x is a floating-point type, and this object is initialized to the integer value obtained by discarding the fractional part ofremove_cvref_t < T > . Initializes the allocator tox .a 13 Throws: Nothing if the effective width of the integer value this object is initialized with is less than or equal to
; otherwise, exceptions thrown during allocation.inplace_bits template < input_range R > requires signed-or-unsigned < ranges :: range_value_t < R >> constexpr explicit basic_big_int ( from_range_t , R && r , const Allocator & a = Allocator ( ) ) ; 14 Effects: Initializes the integer value to an integer value formed by concatenating the base-2 representation of each element in
, where the first element inr holds the least significant part of the concatenated base-2 representation. Ifr is a signed type, the combined base-2 representation is interpreted as that of a signed integer, otherwise as that of an unsigned integer. Initializes the allocator toranges :: range_value_t < R > .a 15 Throws: Nothing if the effective width of the combined integer value is less than or equal to
; otherwise, exceptions thrown during allocation.inplace_bits X.3.4 Operations [big.int.ops]
constexpr span < const uint_multiprecision_t > representation ( ) const noexcept ; 1 Returns: A
representing the range of digits either nested within this object or dynamically allocated, where the first digit in the range has the least significant set of bits. Thespan of the result issize ( ) .div_to_pos_inf ( representation_size ( ) , numeric_limits < uint_multiprecision_t > :: digits )
is added by [P3724R3]. I would expect it to be available by the timediv_to_pos_inf is standardized.big_int 2 Remarks: If the integer value is greater or equal to zero,
shall have the same integer value.basic_big_int ( from_range , representation ( ) , get_allocator ( ) )
[Note: Consequently, elements of typethat are part of the representation must be kept in the correct state, including otherwise extraneous upper bits of magnitude greater than the integer value. This restriction does not apply to elements that are allocated but not part of the representation. — end note]uint_multiprecision_t
This getter single-handedly imposes a huge amount of constraints on the implementation:
needs to store abasic_big_int of dynamically allocated data and ofunion to make the value accessible viauint_multiprecision_t .span - The sign bit is kept separate.
- The padding needs to be kept zero.
constexpr size_type size ( ) const noexcept ; 3 Returns: If the integer value is zero,
; otherwise , where is the integer value.0
The result is identical to for a hypothetical signed integer typestd :: bit_width ( U ( std :: abs ( T ( ) ) ) ) with infinite range and a hypothetical unsigned integer typeT with infinite range. However, this description seems inelegant. It would also be possible to imitate the wording from [bit.pow.two], but with the addition ofU abs/magnitude, we are describing too complicated a math formula in prose.4 Complexity: Constant.
constexpr size_type representation_size ( ) const noexcept ; 5 Returns: If the integer value is zero,
; otherwise1 .div_to_pos_inf ( size ( ) , numeric_ limits<uint_multiprecision_t>::digits) constexpr size_type max_size ( ) const noexcept ; 6 Returns:
.max_representation_size ( ) * numeric_limits < uint_multiprecision_t > :: digits constexpr size_type max_representation_size ( ) const noexcept ; 7 Returns: The maximum number of
objects that can be part of the representation.uint_multiprecision_t constexpr size_type capacity ( ) const noexcept ; 8 Returns:
.representation_capacity ( ) * numeric_limits < uint_multiprecision_t > :: digits constexpr size_type representation_capacity ( ) const noexcept ; 9 Returns:
, wheremax ( inplace_representation_capacity , dynamic-representation-capacity ( ) * numeric_ limits<uint_multiprecision_t>::digits) is the number of currently allocateddynamic-representation-capacity ( ) objects.uint_ multiprecision_tconstexpr allocator_type get_allocator ( ) const noexcept ; 10 Returns: The allocator of this object.
constexpr void reserve ( size_type n ) ; 11 Effects: A directive that informs a
of a planned change in size, so that the storage allocation can be managed accordingly. Reallocation happens at this point if and only if the current capacity is less than the argument ofbasic_big_int .reserve 12 Postconditions:
is greater or equal to the argument ofcapacity ( ) if reallocation happens; and equal to the previous value ofreserve otherwise.capacity ( ) constexpr void reserve_representation ( size_type n ) ; 13 Effects: Equivalent to:
reserve ( n * numeric_limits < uint_multiprecision_t > :: digits ) ; constexpr void shrink_to_fit ( ) ; 14 Effects: If the effective width of the integer value is less than or equal to
,inplace_bits frees the allocation and stores the integer value within theshrink_to_fit object. Otherwise,basic_big_int is a non-binding request to reduceshrink_to_fit tocapacity ( ) . It does not increasesize ( ) , but may reducecapacity ( ) causing reallocations.capacity ( ) 15 Complexity: If the size is not equal to the old capacity, linear in the size of the sequence; otherwise constant.
X.3.5 Modifiers [big.int.modifiers]
constexpr basic_big_int & operator = ( const basic_big_int & x ) ; 1 Effects: Sets the integer value to that of
.x 2 Returns:
.* this constexpr basic_big_int & operator = ( basic_big_int && x ) noexcept ; 3 Effects: Sets the integer value to that of
.x 4 Returns:
.* this template < arbitrary-integer T > constexpr basic_big_int & operator = ( T && x ) noexcept ( no-alloc-constructible-from < T > ) ; 5 Constraints:
isis_same_v < basic_big_int , remove_cvref_t < T >> .false 6 Effects: Sets the integer value to that of
.x 7 Returns:
.* this template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator += ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator -= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator *= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator /= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator %= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator &= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator |= ( T && x ) ; template < common-big-int-type-with < basic_big_int > T > constexpr basic_big_int & operator ^= ( T && x ) ; 8 Effects: Equivalent to:
* this = std :: move ( * this ) @std :: forward < T > ( x ) ; return * this ; where
@is a placeholder for the token in the respective.operator @=template < signed-or-unsigned S > constexpr basic_big_int & operator <<= ( S s ) ; 9 Effects: Equivalent to:
return * this = std :: move ( * this ) << s ; template < signed-or-unsigned S > constexpr basic_big_int & operator >>= ( S s ) ; 10 Effects: Equivalent to:
return * this = std :: move ( * this ) >> s ; constexpr void swap ( basic_big_int & x ) noexcept ( allocator_traits < Allocator > :: propagate_on_container_swap :: value || allocator_traits < Allocator > :: is_always_equal :: value ) ; 11 Effects: Exchanges the integer values of this object and of
.x
What does this do for allocators? X.3.6 Conversions [big.int.conv]
template < class T > constexpr explicit operator T ( ) const noexcept ; 1 Let
be a hypothetical signed integer type with sufficient width to represent theU 's integer value .basic_big_int 2 Constraints:
is a cv-unqualified arithmetic type ([basic.fundamental]).T 3 Returns:
.static_cast < T > ( static_cast < U > ( ) )
[Note: IfisT , the result isbool if is nonzero andtrue otherwise ([conv.integral]). Iffalse is a floating-point type, the result value is determined as if by floating-integral conversion ([conv.fpint]). — end note]T X.3.7 Unary operations [big.int.unary]
constexpr basic_big_int operator + ( ) const & ; 1 Effects: Equivalent to:
return * this ; constexpr basic_big_int operator + ( ) && noexcept ; 2 Effects: Equivalent to:
return std :: move ( * this ) ; constexpr basic_big_int operator - ( ) const & ; 3 Effects: Equivalent to:
return 0 - * this ; constexpr basic_big_int operator - ( ) && noexcept ; 4 Effects: Equivalent to:
return 0 - std :: move ( * this ) ; 5 Complexity: Constant.
6 Remarks: The contents of the representation of the result are identical to those of
prior to the call.representation ( ) constexpr basic_big_int operator ~ ( ) const & ; 7 Effects: Equivalent to:
return - 1 - * this ; constexpr basic_big_int operator ~ ( ) && ; 8 Returns: Equivalent to:
return - 1 - std :: move ( * this ) ; constexpr basic_big_int & operator ++ ( ) & ; 9 Effects: Equivalent to:
return * this += 1 ; constexpr basic_big_int operator ++ ( int ) & ; 10 Effects: Equivalent to:
auto copy = * this ; ++ ( * this ) ; return copy ; constexpr basic_big_int & operator -- ( ) & ; 11 Effects: Equivalent to:
return * this -= 1 ; constexpr basic_big_int operator -- ( int ) & ; 12 Effects: Equivalent to:
auto copy = * this ; -- ( * this ) ; return copy ; X.3.8 Alias
[big.int.alias]big_int using big_int = basic_big_int < see below > ; 1 Result: A specialization of
with an implementation-defined argument for thebasic_big_int constant template parameter, chosen so that equalsmin_inplace_capacity .basic_big_int < > :: inplace_ bits2 Recommended practice: should be sufficiently large so that
may represent the value of all commonly used integer types without allocating.big_int X.3.9 Non-member comparison operator functions [big.int.cmp]
template < class L , common-big-int-type-with < L > R > constexpr bool operator == ( const L & x , const R & y ) noexcept ; 1 Returns:
if the integer value oftrue is equal to the integer value ofx , andy otherwise.false
The requirement means that it's not a valid implementation strategy to wrap any integer innoexcept because that may allocate. Instead, either the integer value or each limb must be compared with the other object. This may involve multi-precision comparisons such as inbasic_big_int .big_int == __int128 template < class L , common-big-int-type-with < L > R > constexpr strong_ordering operator <=> ( const L & x , const R & y ) noexcept ; 2 Returns:
if the integer value ofstrong_ordering :: less is less than the integer value ofx ,y if the integer value ofstrong_ordering :: greater is greater than the integer value ofy , andy otherwise.strong_ordering :: equal X.3.10 Binary operations [big.int.binary]
template < class L , class R > constexpr common-big-int-type < L , R > operator + ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator - ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator * ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator / ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator % ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator & ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator | ( L && x , R && y ) ; template < class L , class R > constexpr common-big-int-type < L , R > operator ^ ( L && x , R && y ) ; 1 Preconditions: For
andoperator / , the integer value ofoperator % is nonzero.y 2 Returns: For each operator function template
whereoperator @@is a placeholder for the respective token, aobject whose integer value is that of performing the operationbasic_big_int , whereT ( x ) @T ( y ) is a hypothetical signed integer type with infinite range.T
[Note: Bitwise operations are performed as if on a two's-complement representation, where positive numbers have an infinite number of leading zeroes, and negative numbers have an infinite number of leading ones. — end note]
has a similar specification withstd :: atomic ; maybe copy the wording from there.operator @template < class T , signed-or-unsigned S > remove_cvref_t < T > operator << ( T && x , S s ) ; 3 Constraints:
is a specialization ofremove_cvref_t < T > .basic_big_int 4 Preconditions:
is greater or equal to zero.s 5 Returns: A
whose integer value is , where is the integer value ofbasic_big_int , and whose allocator is that ofx .x template < class T , signed-or-unsigned S > remove_cvref_t < T > operator >> ( T && x , S s ) ; 6 Constraints:
is a specialization ofremove_cvref_t < T > .basic_big_int 7 Preconditions:
is greater or equal to zero.s 8 Returns: A
whose integer value is rounded towards negative infinity, where is the integer value ofbasic_big_int , and whose allocator is that ofx .x X.3.11 Hash support [big.int.hash]
template < size_t b , class A > struct hash < basic_big_int < b , A >> ; 1 The specialization is enabled ([unord.hash]).
2 Remarks: Let be an object of type
, and let be an object of typebasic_big_int < , > . If and have equal integer value ([big.int.class]), thenbasic_big_int < , > equalshash < basic_big_int < , >> ( ) ( ) .hash < basic_big_int < , >> ( ) ( ) [Note: For an object of integral type
,T can be unequal tohash < T > ( ) ( ) . — end note]hash < big_int > ( ) ( big_int ( ) ) X.3.12 Literals [big.int.literal]
template < char ... digits > constexpr big_int operator " " n ( ) noexcept ( see below ) ; 1 Let be a character sequence obtained by concatenating the elements of
.digits 2 Mandates: matches the syntax of an
integer-literal with nointeger-suffix and containing no digit separators. ([lex.icon])3 Returns: A
object whose integer value is that of interpreted as anbig_int integer-literal .4 Remarks: The function specialization has a non-throwing exception specification if the effective width of the integer value returned by a function call expression is less than or equal to
.big_int :: inplace_bits
This implies we have to perform two-stage parsing. We first parse the literal and see if it can be represented as with SBO. If so, the specialization isbig_int . Otherwise, each invocation of the UDL needs to allocate memory.noexcept Perhaps a good way of implementing this would be to have a variable template
which holdstemplate < char ... digits > big_int_parsed ; , where a value is present only if the pre-parsed value fits instd :: optional < std :: big_int > without allocation.std :: big_int
[charconv]
Change the synopsis [charconv.syn] as follows:
[numeric.int.div]
There is a proposal [P3724R3] in the pipeline which adds the initial set of functions.
[numeric.abs]
currently sits in or in [c.math.abs],
and it seems a bit absurd to require to pull in .
I think it would make more sense for to also expose instead
(it probably already does in many standard libraries),
and then extend the overload set:
constexpr int abs ( int j ) ; constexpr long int abs ( long int j ) ; constexpr long long int abs ( long long int j ) ; 1 Effects: […]
2 Remarks: […]
3
Constraints:
is a specialization of ([bit.int]).
4
Returns:
if the integer value of is negative, and
otherwise.
[numeric.sat.cast]
template < class R , class T > constexpr R saturating_cast ( T x ) noexcept ; 1 Constraints:
andR are signed or unsigned integer types ([basic.fundamental]).T 2 Returns: If
is representable as a value of typex ,R ; otherwise, either the largest or smallest representable value of typex , whichever is closer to the value ofR .x
3
Constraints:
is a signed or unsigned integer type ([basic.fundamental]).
4
Returns:
If the integer value ([big.int.class]) of
is representable as a value of type , ;
otherwise, either the largest or smallest representable value of type ,
whichever is closer to .
for behaves in a saturating
instead of truncating way.
While that behavior is useful,
it would be surprising to C++ users who expect conversions to truncate,
and it would be inconvenient in generic code.
Therefore, can act as an unsurprising spelling.
[numeric.conversions]
Returns:
.
[…]
Returns:
.