<cfenv> Support

This is an expert feature. If you have never used std::fegetround nor std::fesetround before, it is unlikely you will need anything described on this page

<cfenv>

IEEE 754 defines 5 rounding modes for Decimal Floating Point Types.

  1. Downward

  2. To nearest (with ties to even)

  3. To nearest from zero

  4. Toward zero

  5. Upward

The default rounding mode is to nearest with ties to even (#2) as specified in IEEE 754 Section 4.3.3. This is also colloquially known as "Banker’s Rounding"

Using the following enum class and functions you can change the rounding mode from the default at RUNTIME only if BOOST_DECIMAL_NO_CONSTEVAL_DETECTION is not defined.

#include <boost/decimal/cfenv.hpp>

namespace boost {
namespace decimal {

enum class rounding_mode : unsigned
{
    fe_dec_downward,
    fe_dec_to_nearest,
    fe_dec_to_nearest_from_zero,
    fe_dec_toward_zero,
    fe_dec_upward,
    fe_dec_default = fe_dec_to_nearest
};

rounding_mode fegetround() noexcept;

// Returns the rounding mode that has been set
//
// If your compiler defines BOOST_DECIMAL_NO_CONSTEVAL_DETECTION,
// this function will return the default rounding mode
// to alert you that the rounding mode has NOT changed
rounding_mode fesetround(rounding_mode round) noexcept;

} //namespace decimal
} //namespace boost
Much like std::fesetround, boost::decimal::fesetround is not thread safe.

Below is an example of the effects of changing the runtime rounding mode if your compiler supports it:

#include <boost/decimal.hpp>
#include <cassert>

int main()
{
    using namespace boost::decimal::literals;

    auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest

    auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_upward);

    assert(default_rounding_mode != new_rounding_mode);

    const auto lhs {"5e+50"_DF};
    const auto rhs {"4e+40"_DF};

    // With upward rounding the result will be "5.000001e+50"_DF
    // Even though the difference in order of magnitude is greater than the precision of the type,
    // any addition in this mode will result in at least a one ULP difference
    const auto upward_res {lhs + rhs};
    assert(upward_res == "5.000001e+50"_DF);

    boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_downward);

    // Similar to above in the downward rounding mode any subtraction will result in at least a one ULP difference
    const auto downward_res {lhs - rhs};
    assert(downward_res == "4.999999e+50"_DF);

    return 0;
}

As shown, changing the rounding mode WILL change your numerical results. If you are coming from the Intel library (or other C-style libs) where every mathematical function takes a rounding mode, that is not the case in this library; the only way to change the rounding mode for individual operations is via the global rounding mode. Before attempting to change the rounding mode ensure this is actually what you want to happen.

You can similarly change the default rounding mode at compile time with ANY compiler (unlike at runtime) using similarly named macros:

  1. BOOST_DECIMAL_FE_DEC_DOWNWARD

  2. BOOST_DECIMAL_FE_DEC_TO_NEAREST

  3. BOOST_DECIMAL_FE_DEC_TO_NEAREST_FROM_ZERO

  4. BOOST_DECIMAL_FE_DEC_TOWARD_ZERO

  5. BOOST_DECIMAL_FE_DEC_UPWARD

If none of the above macros are defined, the default rounding mode for compile time is the same as the default runtime (i.e. as if BOOST_DECIMAL_FE_DEC_TO_NEAREST were defined). At most ONE of these macros are allowed to be user defined. A #error will be emitted if more than one is detected. The macro must be defined before the inclusion of any decimal library header. The rounding mode set at compile time is thread-safe as it is read only.

This same example can be reduced for the cases where:

  1. Compiler does not support runtime rounding mode changes

  2. You want to change the compile time rounding mode

#define BOOST_DECIMAL_FE_DEC_DOWNWARD
#include <boost/decimal.hpp>

int main()
{
    using namespace boost::decimal::literals;

    constexpr auto lhs {"5e+50"_DF};
    constexpr auto rhs {"4e+40"_DF};
    constexpr auto downward_res {lhs - rhs};
    static_assert(downward_res == "4.999999e+50"_DF, "Incorrectly rounded result");

    return 0;
}
Prior to v5.2.0 this header was <boost/decimal/fenv.hpp>, but has been changed to <boost/decimal/cfenv.hpp> for consistency with the STL naming convention.