**Document number**: P0675R0

**Date**: 2017-06-19

**Reply-to**: John McFarlane, fixed-point@john.mcfarlane.name

**Audience**: SG6, SG14, LEWG

This paper identifies a core set of trait-like definitions necessary to support arbitrary numeric types.

It adds to numeric traits proposal, [P0437], and aims to facilitate the compositional style of numeric type generation described in [P0554]. It contains replacements for the two definitions introduced in [P0381]. An example of a type which greatly benefits from this support is the fixed-point type detailed in [P0037].

The aim is to achieve a high degree of interoperability between fundamental numeric types (such as `int`

), existing custom numeric types (such as `chrono::duration`

and those found in [Boost.Multiprecision]) and future types (such as through the Numerics TS [P0101]) which will be able to apply the types described here.

Interoperability is often assumed to mean the ability to mix different types in algebraic expressions. However, it is also advantageous to compose types from one another (as described in [P0554]). For example, to compose custom types from fundamental integers:

```
// use an unsigned 16-bit integer to approximate a real number with 2 integer and 14 fractional digits
auto pi = fixed_point<uint16_t, -14>{3.141};
assert(pi > 3.1 && pi < 3.2);
// use int to store value gained using accurate rounding mode
auto num_children = rounded_integer<int>{2.6};
assert(num_children == 3);
```

The versatility of such types is increased if they can be composed from one another:

```
// 8-bit type with good rounding characteristics and resolution of 1/16
auto num = fixed_point<rounded_integer<uint8_t>, -4>{15.9375};
```

In order to be generic, these custom types often require information about the underlying type with which they are instantiated. For instance, the signedness of the type might be important:

```
// smart_integer chooses appropriate signedness for results of arithmetic operations
auto a = smart_integer{7u};
auto b = smart_integer{-3};
auto c = a * b; // smart_integer<int>{-21}
```

To determine the signedness of `c`

, `smart_integer`

must know about the signedness of `a`

and `b`

. Because `a`

and `b`

are composed of fundamental types (`unsigned int`

and `signed int`

), `numeric_limits::is_signed`

is already specialized for them. If they were custom numeric types, custom `numeric_limits`

specializations would be required.

But `numeric_limits`

contains only a few of the necessary attributes. For example, once `smart_integer`

has determined that a signed type is required, it may need to convert an existing type:

```
auto m = smart_integer{5u};
auto s = smart_integer{10u};
auto d = m - s; // smart_integer<int>{-5}
```

Here, the representational type of `d`

can be determined using `make_signed_t<uint32_t>`

but `make_signed`

must not be specialized for custom numeric types.

[P0437] proposes a new header, *<num_traits>*, containing free-standing equivalents of most of the attributes currently found in *<limits>*. Crucially, they are user-customizable, thus lifting a heavy restriction on most of the definitions in *<type_traits>*.

This proposal begins the task of extending the contents of */* with definitions that make numeric types more generic. In doing so, it makes it possible for those types to be used to instantiate compositional numeric types.

A parallel can be found in existing support for iterators. Facilities such as `iterator_traits`

and `advance`

provide equivalence between custom integer types and raw pointers. This makes it possible to use efficient language-level features in expressive high-level abstractions.

The following class templates are user-customizable. (Where appropriate, accompanying type aliases ending in `_t`

and variable templates ending in `_v`

are assumed.)

The most common required traits are as follows.

`num_digits`

- Determine the Number of Digits in a Given Type```
template<class T>
struct num_digits;
static_assert(num_digits_v<int64_t> == 63);
```

This trait is a free equivalent of `numeric_limits<T>::digits`

and is present in [P0437].

`set_num_digits`

- Produce a Type With the Desired Number of Digits```
template<class T, int MinDigits>
struct set_num_digits;
static_assert(std::is_same_v<set_num_digits_t<unsigned, 8>, std::uint8_t>);
```

This class complements `num_digits`

and produces a type which is the same as the input except for its width. (Note: it replaces the `set_width`

type proposed in [P0381].)

`is_signed`

/ `make_signed`

/ `make_unsigned`

- Manipulate SignednessThese are all present in *<type_traits>* but user-specialization is not allowed. Either this restriction needs to be lifted or alternative definitions must be found.

Additional definitions which are only required for implementing compositional types are as follows.

`is_composite`

- Determine if Type is Composite```
template<class T>
struct is_composite;
static_assert(!is_composite_v<short>);
static_assert(is_composite_v<fixed_point<short>>);
```

This trait is necessary to distinguish between fundamental types which have no underlying representational value and custom types which conform to the proposed compositional approach.

`to_rep`

- Extract Underlying Value```
template<class T>
struct to_rep;
// a fundamental type is its own representation; it wraps nothing
long r = to_rep<long>()(1L);
// a compositional type is a value wrapper; to_rep unwraps it
long r = to_rep<smart_integer<long>>()(smart_integer<long>{1L});
```

This type is a function object - rather than a type trait. It is used to extract the underlying representational value from the composite type. For fundamental values, it simply returns that value.

If it were specialized for `chrono::duration`

, it would take an object of type `chrono::duration`

and return the result of `chrono::duration::count()`

.

`from_rep`

- Create from Underlying Value```
template<class T>
struct from_rep;
// like to_rep, from_rep leaves its argument unchanged when instantiated with fundamental types
int i = from_rep<int>()(7);
// for compositional types, it can be used to create objects
auto f = from_rep<fixed_point<int, -1>>()(99);
// f is now fixed_point<int, -1>{49.5}
```

The complement to `to_rep`

, this function object is a factory function.

`from_value`

- Determine Type from Initial Value```
template<class T, class Value>
struct from_value;
auto s = from_value_t<fixed_point<int16_t, -1>, unsigned long>{99UL};
// s is now fixed_point<unsigned long, 0>{99}
```

This type trait transforms `T`

into whatever equivalent type best suits assignment from `Value`

. In the case of composite types of `T`

, that will typically mean replacing `T`

's representational type with `Value`

.

In the above example, the fixed-point exponent is also set to zero. This reflects the fact that integers implicitly all have a radix position of zero.

`fixed_point`

Many of the traits presented here were identified during the design of the `fixed_point`

type proposed in [P0037]. Without them, `fixed_point`

can only be specialized for fundamental numeric types which greatly reduces its usefulness. However, this proposal is not a strict prerequisite for [P0037].

Some numeric types which have been prototyped do not require all of the above traits. And it is likely that some numeric types - which have yet to be written - require traits which are not listed here. But it is hoped that this list is a good starting point and will allow users to create many of the types they desire.

All names in this document are placeholders. The choice of `set_digits`

in particular is problematic but no satisfactory alternative has yet found consensus. Use of the term `rep`

comes from `chrono::duration`

.