This type is the safe_float core, it wraps a FP floating point data type. Every operation over this datatype is checked and reported by using the REPORTING_CHECK policy.
A long set of instances for each policy is provided with the safe_float library.
CHECK policies are templates that define the conditions to be evaluated when operating. The only parameter they require is the REPORTER class to do the notifications.
The table shows how the policies defined in the library interact with each operation and danger.
Table 1.1. Table of CHECK policies
Property | Operation | Policy | Example case |
---|---|---|---|
Overflow | Addition | check_addition_overflow | max() + max() |
Overflow | Subtraction | check_subtraction_overflow | lowest() - max() |
Overflow | Multiplication | check_multiplication_overflow | max() * max() |
Overflow | Division | check_division_overflow | max() / min() |
Inexact Rounding | Addition | check_addition_inexact_rounding | lowest() + min() |
Inexact Rounding | Subtraction | check_subtraction_inexact_rounding | max() - lowest() |
Inexact Rounding | Multiplication | check_multiplication_inexact_rounding | (2*((2^digits)-1))*((2^digits)-1) |
Inexact Rounding | Division | check_division_inexact_rounding | 1.0 / 3.0 |
Underflow | Addition | check_addition_underflow | 2.2250738585072019e-308 + -2.2250738585072014e-308 |
Underflow | Subtraction | check_subtraction_underflow | 2.2250738585072019e-308 - 2.2250738585072014e-308 |
Underflow | Multiplication | check_multiplication_underflow | |
Underflow | Division | check_division_underflow | min() / max() |
Division by zero | Division | check_division_by_zero | 1.0 / 0.0 |
Invalid Result (NAN) | Addition | check_addition_invalid_result | infinity() + (- infinity()) |
Invalid Result (NAN) | Subtraction | check_subtraction_invalid_result | infinity() - infinity() |
Invalid Result (NAN) | Multiplication | check_multiplication_invalid_result | infinity() * 0.0 |
Invalid Result (NAN) | Division | check_division_invalid_result | infinity() / infinity() |
Cast to | Assignment | allow_cast_to<T> | T x = sf; |
Cast from | Assignment | allow_cast_from<T> | T x; sf = x; |
The policies can be combined defining classes that inherit from other policies. This requires some glue code in the case the policies combined are not 100% orthogonal, this glue is implemented boost::safe_float::policy::compose.
The following is a table of convenient combined policies already available in the library and their components.
Table 1.2. Table of CHECK composed policies
Property | Operation | Policy | Components |
---|---|---|---|
Overflow | All Arithmetic | check_overflow | check_{addition,subtraction,multiplication,division}_overflow |
Inexact Rounding | All Arithmetic | check_inexact_rounding | check_{addition,subtraction,multiplication,division}_inexact_rounding |
Underflow | All Arithmetic | check_underflow | check_{addition,subtraction,multiplication,division}_underflow |
Invalid Result | All Arithmetic | check_invalid_result | check_{addition,subtraction,multiplication,division}_nan |
Bothflows | All arithmetic | check_bothflows | check_{under,over}_flow |
All | All arithmetic | check_all | check_{bothflow,division_by_zero,inexact_rounding,nan} |
For combining the Policies a composer function is used called policy::compose, the function receives variadic template list of policies and returns a new policy type.
The composed policy, will call sequentially each pre and post condition on each policy and return false if any check returns false, and true when every check returned true.
The composed policies listed in previous check are defined using the policy::compose in the convenience header file.
Reporting classes are focus in how a validation failure is notified to the user.
Options available out of the box are:
on_fail_throw : Throws a safe_float_exception when the checks fail.
on_fail_abort : Calls std::abort() when the check fails.
on_fail_assert : Inserts an assert for each condition, it makes safe the debuging only.
on_fail_log : This logs each error into a stream that needs to be declared and silently continues its execution.
on_fail_cerr : This is a particular case of on_fail_log. Here the log is output to stadard error.
on_fail_unexpected (experimental) : This adds support to wrap safe_float into a Boost.Expected class, and return unexpected on check fail.
Extending the list of options is as easy as defining a class with the method: static void report_failure(const std::string& message);
In safe_float a new specialization of numeric_limits is defined for each wrapped number. For most functions and constants the values are obtained from the original number.
The few methods/constants that are not aliased to the wrapped type are listed below.
is_exact is true if the wraped type is or if all operation are checked for inexact results by the wrapper. Same applies to round_style and round_error.
has_quiet_NaN is true if the wraped type has quiet NaN and no check is introduced by the wrapper to catch them.
has_signaling_NaN is true if the wraped type has signaling NaN and no check is introduced by the wrapper to catch them.
Functions returning a safe_float: min, max, lowest, quit_NaN, signaling_NaN, epsilon, round_error, infinity, denorm_min. They return the value returned by the wrapped type wrapped by safe_float.
Each CHECK policy is implemented using fenv functions and without using fenv functions. The selection of which implementation is compiled is selected by the FENV_AVAILABLE constant. This constant is passed to the compiler automaticly by the b2 script if the pragma to make them safe is available. This convenient detection was not implemented in cmake scripts yet.