**Author:**
Fernando Cacciola

**Contact:** fernando.cacciola@gmail.com

**Organization: **SciSoft

**Date:**
2005-08-29

**Number:**
N1879=05-0139

An **out-of-range** conversion occurs when a source value cannot be
presented in a destination type, not even approximately between 2 adjacent
destination values.

If the destination type is unsigned, the conversion, even if logically out-of-range, if always defined (4.7.2) as the least congruent unsigned value.

If the destination type is a signed integer, the result of an out-of-range conversion is implementation-defined (4.7.3).

If the destination type is floating-point, or the source type is floating-point and the destination type integer, an out-of-range conversion is undefined-behavior (4.8.1,4.9.1).

Clearly, out-of-range conversions are a problem because they can involve from implementation-defined to undefined behavior. Even signed to unsigned conversion can be a problem because the resulting value, even if well defined, can be as wrong as any arbitrary unrelated value.

Numerical applications, ranging from sophisticated scientific software to modeling to accounting to simple number manipulation can rarely disregard the problems associated with out-of-range conversions. In consequence, most such applications incorporate ad-hoc out-of-range checking code and protocols.

Furthermore, most C++ users are unaware of these rules. For instance, most C++ programmers are unaware that an out-of-range floating point conversion is undefined behavior (...and can for instance cause the client's hard disk to be wiped out entirely, as the say goes)

Generic Programming came into C++ along with the need for a numerical conversion utility providing well-defined behavior for all generic conversion cases (all combinations of source and destination types). So much in fact, that the Boost project launched, 6 years ago, with such an utility as part of its initial offering.

Whether `boost::numeric_cast<>`

or any other in-house facility, most
numerical applications have been using range-checked numeric conversions from
a long time.

But there is a problem: properly determining whether a source value is in
range for a given destination type is tricky because involves comparing a
value of a type with the boundary values of a different type, task that itself
requires a numeric conversion. This is in fact so difficult that `boost::numeric_cast<>`

had it wrong for certain cases for years, even after
a number of revisions.

A `numeric_cast<>`

is therefore a perfect candidate for
standardization because
it is widely needed but its implementation much too difficult to ask users to
roll their own (or *need* one from a non standard library)

The Boost Numeric Conversion Library introduced
a re-implementation of `boost::numeric_cast<>`

which using metaprogramming fixed all
the range-checking problems of the initial version and even gained
compile-time optimizations by totally avoiding the range-check when
unnecessary.

That library, which serves as a reference implementation, developed the right concepts that are needed to formulate a proper range-checking logic for any combination of types (that is, a formulation that prevents the conversions needed by the check itself to be out-of-range). It also shown that template-metaprogramming can be used to optimize away the range-checking when unneeded.

The Boost library uses a policy-based design that offers users a wide
latitude of choices (user-defined-types support, custom out-of-range handling,
float to integer rounders, etc), but the facility proposed in this paper for
standardization is a simplified version more in the spirit of the original
`boost numeric_cast<>`

The proposed `numeric_cast<T>(s)`

shall give the same result as
`static_cast<T>(s)`

(that is, the conversion itself is a just standard
conversion) but with an added out-of-range check which implementations are
required to bypass if unneeded (the proposed text defines exactly when is this
the case). The out-of-range check is itself performed separately by
another proposed function: `bool is_out_of_range<T>(s)`

which users can call if they
need a domain-specific out-of-range response. If `numeric_cast<T>(s)`

detects an
out of range condition, an out-of-range *installable handler* is called.

The proposed text is intended to allow upcoming numeric types like
decimal types, big integers, rationals, etc to be usable with the facility.
To that effect, the proposed text includes some definitions which encompasses these
eventually-standard types. Additionally, the proposed text intends to allow a
C++ program to extend the standard `numeric_cast<>`

for user-defined-types.

Nonetheless, even though the facility must interact with other pieces of the standard, the proposal takes the form of a pure extension. There are only additions.

The fundamental need that this proposal aims to satisfy is providing
range-checked *standard* conversions because range checking is a general
need in a wide range of application domains. But there are other needs: for
example, in converting a floating-point type to an integer type it might be
critical to be able to control how the fractional part is handled (instead of
simply discarding it as a standard conversion does), or maybe an application
needs to trap *inexact* (but in-range) conversions (when the source value is
represented as one of the two adjacent values in the destination type); or
"fix" out of range conversions by choosing the closest boundary value in the
destination type. Yet from all this features, range checking is the only one
that doesn't need to redefine the conversion itself because it can be provided as
a *precondition* to a standard conversion.

It is certainly possible to design a facility to give users further control over the conversion process. To some extent, C99 and the Boost Numeric Conversion Library do that. However, the pieces that would be directly involved and affected are yet to be standardized (additions from C99, additional numeric types, like decimal, integer, rational, etc..) so is more convenient to take it one step at a time and standardize at this point only the simplest solution to the more general problem: range checking.

A policy-based design like that used in the Boost Numeric Conversion library is certainly a reasonable way to give users the extra latitude, but such designs has as a drawback that the resulting behavior is on the hands of the users. For such a fundamental part of a language as its standard library, just carefully specifying the requirements and expected behavior for the policies is simply not enough. There is no discussion about the potential and usefulness of policies, but the actual instrumentation of such designs needs tools from the language, like concepts and design-by-contract forms, which are not yet available (but these have been already formally proposed). Thus, the policy-based design of the Boost version has been dropped in favor of a simpler but better defined facility.

The canonical way to treat exceptional errors in C++ is by throwing an
exception. However, numeric conversions are specially important in numeric
applications and numeric applications are particularly common in restricted
platforms like embedded systems. In consequence, always throwing an exception in
the presence of an out-of-range conversion is unlikely to be welcome by the
numerical community which is intended to be a primary target. Thus, the
proposed solution uses an *installable handler* (like the new handler) so
that implementations and programs can customize the out-of-range response to
suit the target platform.

At this point in the C++ standard life-cycle, a number of numeric types are
being proposed. This proposal assumes that at least some of these types will
be accepted so it tries to cover the conversions involving not only *
arithmetic types* but these eventual *standard numeric types* as well.
Since the proposed `numeric_cast<>()`

only adds range checking but doesn't
define the conversion itself, the proposal only has to properly cover the
range checking logic for any type it intends to specify behavior. To achieve
that, the specification of the range checking logic that implementations must
follow is formulated with the assumption that the implementation has complete
access to the details of these *standard numeric types*. In particular,
the implementation is required to be able to test whether a boundary value of
any given *standard numeric type* is representable in any other given *standard numeric type*.

Furthermore, the proposal adds a simple provision to allow C++ programs to
make their user-defined-types be usable with `numeric_cast<>`

: they are allowed to
provide additional specializations, even partial (assuming the language eventually allows that), of the template function.

*A*. Add the following to 17.4.3.5 [lib.handler.functions]

`out_of_range_handler`

`set_of_out_range_handler`

*B*. Add the following to the synopsis for <numeric> at 26.4,
[lib.numeric.ops]

template<class T, class S> bool is_out_of_range ( S s ) throw() ;

`template<class T, class S> T numeric_cast ( S s ) ``throw ( range_error ) `

;

*C*. Add the following subclauses:

any type which satisfies the *numeric type requirements* (26.1)

[*Note:* a user defined type can fit this definition *-end
note*]

any *numeric type* (17.1.21) defined in this standard, including
library provided types such as complex<>

any conversion, standard or user defined, involving *numeric types*
(17.1.21)

any *numeric conversion* (17.1.23) involving *standard numeric types*
(17.1.22)

*standard numeric conversions* from any value of a* standard numeric type* (17.1.2) S to a
value of a *standard numeric
type* T such that either numeric_limits<T>::is_bounded==false, or both `numeric_limits<S>::lowest()`

^{1}` and numeric_limits<S>::max()`

can be represented in the type T, either exactly or between two adjacent T
values. That is, conversions for which any value of the source type can be
represented in the destination type.

*standard numeric conversions* from any value of a* standard numeric type* (17.1.2) S to a
value of a *standard numeric
type* T such that mumeric_limits<T>::is_bounded==true and `numeric_limits<S>::lowest()`

^{1}```
or numeric_limits<S>::max()
```

cannot be represented in the type T, neither exactly nor between two adjacent T
values. That is, conversions for which some values of the source type cannot
be represented in the destination type.

If conversions from S to T are *sub-ranged*, then necessarily,
conversions in the opposite direction, T->S, are *super-ranged*. The
converse is not necessarily true (for example, a conversion from N->N is
super-ranged in both directions)

typedef void (*out_of_range_handler)();

1. The type of the *handler function* called by
numeric_cast<> (26.4.5) when an out of range is detected

2. Required behavior: an *out_of_range_handler*
shall perform one of the following::

- throw an exception of type *
range_error* (19.1.7) or a class derived from *
range_error*

- call either abort() or exit()

- return

3. An out of range handler installed by a program or the implementation is allowed to perform additional operations, with side effects, prior to the required operation, provided they do not incur undefined behavior.

[*Note:* allowing additional operations is intended
to let a C++ program properly register and report the incident; not to attempt to
recover from the error *-endnote*]

out_of_range_handler set_out_of_range_handler( out_of_range_handler new_h ) throw() ;

1. *Effects:* Establishes the function designated by *new_h* as the current *out_of_range_handler*.

2. * Returns:* 0 on first call, the previous out_of_range_handler on subsequent calls.

template<class T, class S> bool is_out_of_range ( Source s ) throw() ;

1. Returns:

A. If conversions from S
to T are *super-ranged* (17.1.22), false for any value of s [*Note*:
this case can only be applied if both T and S are *standard
numeric types *(17.1.22), per definition of *super-ranged* *
-end note*]

B. If conversions from S
to T are *sub-ranged* (17.1.23); the result of evaluating this
expression:

` (s >= static_cast<S>(numeric_limits<T>::lowest())`

^{1}

&& (s <= static_cast<S>(numeric_limits<T>::max ()).

[*Note*:
this case can only be applied if both S and T are *standard numeric types
*(17.1.22), per definition of *sub-ranged* *-end note*]

[*Note*:
when S->T are *sub-ranged*; T->S are *super-ranged* so the expression can be
evaluated without incurring undefined behavior]

C.If either S or t are not *
standard numeric types *(17.1.22), an unspecified
result.

2. A C++ program is allowed to defined specializations of
this template function provided that S or T or both are*
user-defined-types. *If both S and T are *standard
numeric type *(17.1.22)* *the specializations ill-formed and the implementation is
required to issue diagnostic.

```
template<class T, class S> T numeric_cast( S s ) throw (
range_error );
```

1. The result of the expression `numeric_cast<T>(s)`

is the same as the result of the expression `static_cast<T>(s)`

with the
following added restrictions and side-effects:

2. If either S or T are not *standard numeric types *(17.1.22), the
behavior is unspecified
if there exist in the program a user-provided specialization of the function
for these types and undefined if such a user-provided specialization
does not exist.

4. If
conversions from S to T are *sub-ranged* (17.1.23), is_out_of_range<T>(s) shall be
called and if the result of that is true, the installed out_of_range-handler
shall be invoked, if any.

5. If
conversions from S to T are *super-ranged* (17.1.22), is_out_of_range<T>(s) shall
*not* be called and numeric_cast<T>(s) shall be distinguishable from static_cast<T>(s) only in the additional type requirements. That is,
implementations are required to skip the overhead of range checking for
super-ranged conversions.

6. A C++ program is allowed to defined specializations of
this template function provided that S or T or both are*
user-defined-types. *If both S and T are *standard
numeric type *(17.1.22)* *the specialization is ill-formed and the implementation is
required to issue a diagnostic.

7. The implementation is allowed to implement
`numeric_cast<>`

as an internal operator and not as a explicit library function
provided it can diagnose a violation to subclause 6 in case a C++ programs
adds specializations.

__Footnotes: __

*lowest() is the addition to numeric_limits<> proposed in N1881* (which accompanies this proposal)

All the people in the boost community, particularly those involved in the development of the Boost.NumericConversion library.

Thorsten Ottosen for his help preparing this proposal.

- Boost.Numeric Conversion Library, http://www.boost.org/libs/numeric/conversion/doc/index.html, Fernando Cacciola
- Standard Documents:
- ISO/IEC 9899:1999 (C99 Standard)
- ISO/IEC 10967-1 (Language Independent Arithmetic (LIA), Part I, 1994)
- ISO/IEC 2382-1:1993 (Information Technology - Vocabulary - Part I: Fundamental Terms)
- ANSI/IEEE 754-1985 [and IEC 60559:1989] (Binary floating-point)
- ANSI/IEEE 854-1988 (Radix Independent floating-point)
- ANSI X3/TR-1-82 (Dictionary for Information Processing Systems)
- ISO/IEC JTC1/SC22/WG14/N753 C9X Revision Proposal: LIA-1 Binding: Rationale

- Papers:
- David Goldberg What Every Computer Scientist Should Know About Floating-Point Arithmetic
- Prof. William Kahan papers on floating-point.