Design Decisions and Standards Deviations :: Boost.Decimal

# Design Decisions and Standards Deviations

## C++14 Requirement

### Constexpr Support

Using C++14’s relaxed definition of `constexpr` nearly all functionality of the library can be constexpr. This allows Boost.Decimal to better emulate the functionality a built-in type would have.

### Boost.Math Integration

Boost.Math requires C++14 as the minimum language standard for the library. By meeting this requirement, and Boost.Math’s conceptual requirements for a user-defined type we are able to provide the statistics, interpolation, quadrature, etc. out of the box.

## IEEE Deviations

### `<cmath>` Functions Do Not Meet the Definition of Correctly Rounded

IEEE 754 specifies for correct rounding: "This standard’s method of converting an infinitely precise result to a floating-point number, as determined by the applicable rounding direction. A floating-point number so obtained is said to be correctly rounded." None of these functions meet the definition of correctly rounded (which would imply precision of 0.5 ULP). From the Intel libdfp README: "binary floating-point mathematical functions implemented for example in C or other compiler math libraries are in general not correctly rounded either". Technically IEEE 754 differentiates the specification for mandatory operations (e.g. `fma`, `sqrt`, etc.) in Clause 5, and recommended operations (e.g. `exp`, `log`, etc.) in Clause 9, but our statement about half ULP precision applies to all non-boolean result `<cmath>` functions. This does not apply to Clause 5 functions like `isnan` which has a boolean result because ULP precision does not apply.

### Floating Point Exception Flags are not Supported

The normal method in C++ to set and get the current floating point exception flags is via `std::fegetexceptflag` and `std::fesetexceptflag`. The decision then becomes do we support `constexpr` or floating point exceptions? We find that the former is far more useful than the latter. The alternative to would be to use C style functions such as:

    decimal64_t dec64_add(decimal64_t a, decimal64_t b, rounding_mode round, int* floating_point_flags);

This style of function is used in Intel libdfp because it is a C library. We find this to be unidiomatic and unergonomic for a modern C++ library.

## C++ Standards Deviations

### `from_chars` Distinguishes Overflow from Underflow

`std::from_chars` has an open issue with LWG here: https://cplusplus.github.io/LWG/lwg-active.html#3081. The standard for <charconv> does not distinguish between underflow and overflow like strtod does. `boost::decimal::from_chars` modifies `value` in order to communicate this to the user in a divergence from the standard. This behavior is the same as that of `boost::charconv::from_chars_erange`.

### `from_chars` Supports Rounding Mode

The C++ specification states that `from_chars` resulting value is to be rounded to nearest. Since the types in this library are more sensitive to rounding mode differences, `from_chars` rounds using the current global rounding mode as reported by `fegetround()`.

### `from_chars` and `to_chars` both have an additional `chars_format` option

In order to support an IEEE 754 requirement to print a value to include its cohort information we introduced an additional `chars_format` option, `cohort_preserving_scientific`. Cohort preservation only makes sense in scientific format because it is the actual representation of the layout of the type, and thus no other cohort preserving formats are supported.

### `istream` of Non-finite Values is Allowed

Unlike built-in binary floating point types the library allows input of non-finite values such as the below:

    std::istringstream is("INF");
    decimal32_t x;
    is >> x;
    const auto endpos {is.tellg()};

    assert(isinf(x));
    assert(endpos == 3);

The result of `tellg()` will only parse until a complete non-finite value is found and then terminate. For example `nanfinity` will construct a `NAN` and set `endpos = 3`, or `nan(snan)JUNK` will construct `SNAN` and set `endpos = 9` since `nan(snan)` contains a valid payload. This is designed to match the value of `r.ptr` in `<charconv>`.

### Locale Facets are Unsupported

Facets as discussed in N2849 section 3.10 are unimplemented.