Design Decisions and Standards Deviations
C++14 Requirement
No Binary Floating Point Operations
The library deliberately does not contain any operations between a binary floating point type and the decimal floating point types. The rationale is similar to that of the library in the first place in that you may end up with surprising results. Conversion operators and constructors are available for all decimal types should the user want to explicit cast one side of the operation to conduct the operation.
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.