Overview
Description
Boost.Decimal is an implementation of IEEE 754 and ISO/IEC DTR 24733 Decimal Floating Point numbers. The library is header-only, has no dependencies, and requires C++14.
Motivation
Current C++ floating point types store the significand (often incorrectly referred to as the mantissa) as binary digits. Famously this leads to representation errors: https://0.30000000000000004.com. Decimal floating point numbers avoid this issue by storing the significand in base-10 (decimal). The other major difference between binary and decimal floating point types is that the latter allows for multiple representations of the same number. For example 1e5 could also be stored as 0.1e6, 0.01e7, so on and so forth. These are referred to as cohorts which binary does not have as there is only one way to represent each number in binary floating point.
Use Cases
The use case for Decimal Floating Point numbers is where rounding errors are significantly impactful such as finance. In applications where integer or fixed-point arithmetic are used to combat this issue Decimal Floating Point numbers can provide a significantly greater range of values. For example, while a fixed-point representation that allocates 8 decimal digits and 2 decimal places can represent the numbers 123456.78, 8765.43, 123.00, and so on, a floating-point representation with 8 decimal digits could also represent 1.2345678, 1234567.8, 0.000012345678, 12345678000000000, and so on.
Supported Compilers
Boost.Decimal is tested on Ubuntu (x86_64, s390x, and aarch64), macOS (x86_64, and Apple Silicon), and Windows with the following compilers:
-
GCC 7 and later
-
Clang 6 and later
-
Visual Studio 2017 and later
Tested on Github Actions and Drone. Coverage can be found on Codecov.
Basic Usage
#include <boost/decimal.hpp>
#include <iostream>
#include <iomanip>
int main()
{
using namespace boost::decimal;
// Outputs 0.30000000000000004
std::cout << std::setprecision(17) << 0.1 + 0.2;
// Construct the two decimal values
constexpr decimal64 a {1, -1}; // 1e-1 or 0.1
constexpr decimal64 b {2, -1}; // 2e-1 or 0.2
// Outputs 0.30000000000000000
std::cout << a + b << std::endl;
return 0;
}
Decimal Types
Synopsis of header <boost/decimal.hpp>
// Paragraph numbers are from ISO/IEC DTR 24733
namespace boost {
namespace decimal {
// 3.2.2 class decimal32:
class decimal32;
// 3.2.3 class decimal64:
class decimal64;
// 3.2.4 class decimal128:
class decimal128;
// 3.2.7 unary arithmetic operators:
constexpr decimal32 operator+(decimal32 rhs) noexcept;
constexpr decimal64 operator+(decimal64 rhs) noexcept;
constexpr decimal128 operator+(decimal128 rhs) noexcept;
constexpr decimal32 operator-(decimal32 rhs) noexcept;
constexpr decimal64 operator-(decimal64 rhs) noexcept;
constexpr decimal128 operator-(decimal128 rhs) noexcept;
// 3.2.8 binary arithmetic operators:
// LHS and RHS can be any integer or decimal type
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal32 rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal64 rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal128 rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal32 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal64 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal128 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal32 rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal64 rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal128 rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal32 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal64 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal128 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal32 rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal64 rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal128 rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal32 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal64 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal128 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal32 rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal64 rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal128 rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal32 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal64 lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal128 lhs, RHS rhs) noexcept;
// 3.2.9 comparison operators:
// LHS and RHS can be any integer or decimal type
constexpr bool operator==(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator==(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal128 lhs, RHS rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator!=(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal128 lhs, RHS rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator<(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal128 lhs, RHS rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator<=(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal128 lhs, RHS rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator>(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal128 lhs, RHS rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal32 rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal64 rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal128 rhs) noexcept;
constexpr bool operator>=(decimal32 lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal64 lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal128 lhs, RHS rhs) noexcept;
// If C++20 is available
// LHS and RHS can be any integer or decimal type
constexpr std::partial_ordering operator<=>(decimal32 lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(decimal64 lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(decimal128 lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal32 rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal64 rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal128 rhs) noexcept;
// 3.2.10 Formatted input:
// These functions are locale dependent
template <typename charT, typename traits>
std::basic_istream<charT, traits>&
operator>>(std::basic_istream<charT, traits>& is,
decimal32& d);
template <typename charT, typename traits>
std::basic_istream<charT, traits>&
operator>>(std::basic_istream<charT, traits> & is,
decimal64& d);
template <typename charT, typename traits>
std::basic_istream<charT, traits>&
operator>>(std::basic_istream<charT, traits> & is,
decimal128& d);
// 3.2.11 Formatted output:
// These functions are locale dependent
template <typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
decimal32 d);
template <typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
decimal64 d);
template <typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
decimal128 d);
} //namespace decimal
} //namespace boost
3.2.8 Note
In the event of binary arithmetic between a non-decimal type and a decimal type the arithmetic will occur between the native types, and the result will be returned as the same type as the decimal operand. (e.g. decimal32 * uint64_t → decimal32)
In the event of binary arithmetic between two decimal types the result will be the higher precision type of the two (e.g. decimal64 + decimal32 → decimal64)
Decimal32
Description
Decimal32 is the 32-bit version of the decimal interchange format, and has the following properties as defined in IEEE 754-2019 table 3.6
-
Storage width - 32 bits
-
Precision - 7 decimal digits (not bits like binary)
-
Max exponent - 96
-
Max Value - 9.999999e96
-
Smallest normalized value - 1.000000e-95
-
Smallest subnormal - 1e-101
#include <boost/decimal/decimal32.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.2.1 construct/copy/destroy
constexpr decimal32() noexcept = default;
// 3.2.2.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal32(Float val) noexcept;
// 3.2.2.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal32(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal32(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal32& operator=(const Integeral& RHS) noexcept;
// 3.2.2.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.2.5 increment and decrement operators:
constexpr decimal32& operator++();
constexpr decimal32 operator++(int);
constexpr decimal32& operator--();
constexpr decimal32 operator--(int);
// 3.2.2.6 compound assignment:
constexpr decimal32& operator+=(RHS rhs);
constexpr decimal32& operator-=(RHS rhs);
constexpr decimal32& operator*=(RHS rhs);
constexpr decimal32& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal64() const noexcept;
explicit constexpr operator decimal128() const noexcept;
} //namespace decimal
} //namespace boost
Decimal64
Description
Decimal64 is the 64-bit version of the decimal interchange format, and has the following properties as defined in IEEE 754-2019 table 3.6
-
Storage width - 64 bits
-
Precision - 16 decimal digits (not bits like binary)
-
Max exponent - 385
-
Max Value - 9.999999999999999e385
-
Smallest normalized value - 1.000000000000000e-382
-
Smallest subnormal - 1e-398
#include <boost/decimal/decimal64.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.3.1 construct/copy/destroy
constexpr decimal64() noexcept = default;
// 3.2.2.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal64(Float val) noexcept;
// 3.2.3.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal64(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal64(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal64& operator=(const Integeral& RHS) noexcept;
// 3.2.3.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.3.5 increment and decrement operators:
constexpr decimal64& operator++();
constexpr decimal64 operator++(int);
constexpr decimal64& operator--();
constexpr decimal64 operator--(int);
// 3.2.3.6 compound assignment:
constexpr decimal64& operator+=(RHS rhs);
constexpr decimal64& operator-=(RHS rhs);
constexpr decimal64& operator*=(RHS rhs);
constexpr decimal64& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal32() const noexcept;
explicit constexpr operator decimal128() const noexcept;
} //namespace decimal
} //namespace boost
Decimal128
Description
Decimal128 is the 128-bit version of the decimal interchange format, and has the following properties as defined in IEEE 754-2019 table 3.6
-
Storage width - 128 bits
-
Precision - 34 decimal digits (not bits like binary)
-
Max exponent - 6145
-
Max Value - 9.99999…e6145
-
Smallest normalized value - 1.0000…e-6142
-
Smallest subnormal - 1e-6176
#include <boost/decimal/decimal128.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.4.1 construct/copy/destroy
constexpr decimal128() noexcept = default;
// 3.2.4.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal128(Float val) noexcept;
// 3.2.4.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal128(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal128(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal128& operator=(const Integeral& RHS) noexcept;
// 3.2.4.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.4.5 increment and decrement operators:
constexpr decimal128& operator++();
constexpr decimal128 operator++(int);
constexpr decimal128& operator--();
constexpr decimal128 operator--(int);
// 3.2.4.6 compound assignment:
constexpr decimal128& operator+=(RHS rhs);
constexpr decimal128& operator-=(RHS rhs);
constexpr decimal128& operator*=(RHS rhs);
constexpr decimal128& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal32() const noexcept;
explicit constexpr operator decimal64() const noexcept;
} //namespace decimal
} //namespace boost
Fast Types
Now that we have seen the three basic types as specified in IEEE-754 there are three additional adjacent types: decimal32_fast
, decimal64_fast
, and decimal128_fast
.
These types yield identical computational results, but with ~3x faster performance.
These types make the classic tradeoff of space for time as they require more storage width than the IEEE 754 conformant type.
Decimal32_fast
Description
decimal32_fast
has the same ranges of values and representations as decimal32
but with greater performance.
The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type.
As is often the case this trades space for time by having greater storage width requirements.
-
Storage width - At least 48bits (
std::uint_fast32_t
+std::uint_fast8_t
+bool
) -
Precision - 7 decimal digits (not bits like binary)
-
Max exponent - 96
-
Max Value - 9.999999e96
-
Smallest normalized value - 1.000000e-95
-
Smallest subnormal - 1e-101
#include <boost/decimal/decimal32_fast.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.2.1 construct/copy/destroy
constexpr decimal32_fast() noexcept = default;
// 3.2.2.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast(Float val) noexcept;
// 3.2.2.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal32_fast(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal32_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal32_fast& operator=(const Integeral& RHS) noexcept;
// 3.2.2.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.2.5 increment and decrement operators:
constexpr decimal32_fast& operator++();
constexpr decimal32_fast operator++(int);
constexpr decimal32_fast& operator--();
constexpr decimal32_fast operator--(int);
// 3.2.2.6 compound assignment:
constexpr decimal32_fast& operator+=(RHS rhs);
constexpr decimal32_fast& operator-=(RHS rhs);
constexpr decimal32_fast& operator*=(RHS rhs);
constexpr decimal32_fast& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal64() const noexcept;
explicit constexpr operator decimal128() const noexcept;
} //namespace decimal
} //namespace boost
Decimal64_fast
Description
decimal64_fast
has the same ranges of values and representations as decimal64
but with greater performance.
The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type.
As is often the case this trades space for time by having greater storage width requirements.
-
Storage width - At least 88 bits (
std::uint_fast64_t
+std::uint_fast_16_t
+bool
) -
Precision - 16 decimal digits (not bits like binary)
-
Max exponent - 385
-
Max Value - 9.999999999999999e385
-
Smallest normalized value - 1.000000000000000e-382
-
Smallest subnormal - 1e-398
#include <boost/decimal/decimal64_fast.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.3.1 construct/copy/destroy
constexpr decimal64_fast() noexcept = default;
// 3.2.2.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast(Float val) noexcept;
// 3.2.3.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal64_fast(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal64_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal64_fast& operator=(const Integeral& RHS) noexcept;
// 3.2.3.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.3.5 increment and decrement operators:
constexpr decimal64_fast& operator++();
constexpr decimal64_fast operator++(int);
constexpr decimal64_fast& operator--();
constexpr decimal64_fast operator--(int);
// 3.2.3.6 compound assignment:
constexpr decimal64_fast& operator+=(RHS rhs);
constexpr decimal64_fast& operator-=(RHS rhs);
constexpr decimal64_fast& operator*=(RHS rhs);
constexpr decimal64_fast& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal32() const noexcept;
explicit constexpr operator decimal128() const noexcept;
} //namespace decimal
} //namespace boost
Decimal128_fast
Description
decimal128_fast
has the same ranges of values and representations as decimal128_fast
but with greater performance.
The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type.
As is often the case this trades space for time by having greater storage width requirements.
-
Storage width - at least 168 bits (
uint128
+std::uint_fast32_t
+bool
) -
Precision - 34 decimal digits (not bits like binary)
-
Max exponent - 6145
-
Max Value - 9.99999…e6145
-
Smallest normalized value - 1.0000…e-6142
-
Smallest subnormal - 1e-6176
#include <boost/decimal/decimal128_fast.hpp>
namespace boost {
namespace decimal {
// Paragraph numbers are from ISO/IEC DTR 24733
// 3.2.4.1 construct/copy/destroy
constexpr decimal128_fast() noexcept = default;
// 3.2.4.2 Conversion form floating-point type
template <typename Float>
explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast(Float val) noexcept;
// 3.2.4.3 Conversion from integral type
template <typename Integer>
explicit constexpr decimal128_fast(Integer val) noexcept;
template <typename Integral1, typename Integral2>
constexpr decimal128_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept;
template <typename Integral>
constexpr decimal128_fast& operator=(const Integeral& RHS) noexcept;
// 3.2.4.4 Conversion to integral type
explicit constexpr operator int() const noexcept;
explicit constexpr operator unsigned() const noexcept;
explicit constexpr operator long() const noexcept;
explicit constexpr operator unsigned long() const noexcept;
explicit constexpr operator long long() const noexcept;
explicit constexpr operator unsigned long long() const noexcept;
explicit constexpr operator std::int8_t() const noexcept;
explicit constexpr operator std::uint8_t() const noexcept;
explicit constexpr operator std::int16_t() const noexcept;
explicit constexpr operator std::uint16_t() const noexcept;
// 3.2.4.5 increment and decrement operators:
constexpr decimal128_fast& operator++();
constexpr decimal128_fast operator++(int);
constexpr decimal128_fast& operator--();
constexpr decimal128_fast operator--(int);
// 3.2.4.6 compound assignment:
constexpr decimal128_fast& operator+=(RHS rhs);
constexpr decimal128_fast& operator-=(RHS rhs);
constexpr decimal128_fast& operator*=(RHS rhs);
constexpr decimal128_fast& operator/=(RHS rhs);
// 3.2.6 Conversion to floating-point type
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept;
explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept;
// The following are available assuming a C++23 compiler that provides the header <stdfloat>
explicit constexpr operator std::float16_t() const noexcept;
explicit constexpr operator std::float32_t() const noexcept;
explicit constexpr operator std::float64_t() const noexcept;
explicit constexpr operator std::bfloat16_t() const noexcept;
explicit constexpr operator decimal32() const noexcept;
explicit constexpr operator decimal64() const noexcept;
} //namespace decimal
} //namespace boost
Fast Type Conversions
Since we have non-IEEE 754 compliant types we offer a set of functions that allow their conversion to and from the IEEE 754 compliant BID layout. These functions allow lossless conversion with more compact storage.
namespace boost {
namespace decimal {
namespace detail {
struct uint128
{
std::uint64_t hi;
std::uint64_t lo;
};
} // namespace detail
BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32(decimal32 val) noexcept;
BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32f(decimal32_fast val) noexcept;
BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64(decimal64 val) noexcept;
BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64f(decimal64_fast val) noexcept;
BOOST_DECIMAL_CXX20_CONSTEXPR detail::uint128 to_bid_d128(decimal128 val) noexcept;
template <typename T>
BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(T val) noexcept;
template <typename T = decimal32_fast>
BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint32_t bits) noexcept;
template <typename T = decimal64_fast>
BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint64_t bits) noexcept;
template <typename T = decimal128>
BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(detail::uint128 bits) noexcept;
} // namespace decimal
} // namespace boost
Literals Support
The following literals are offered to construct each of the types:
namespace boost {
namespace decimal {
constexpr auto operator "" _DF(const char* str) -> decimal32
constexpr auto operator "" _df(const char* str) -> decimal32
constexpr auto operator "" _DF(unsigned long long v) -> decimal32
constexpr auto operator "" _dF(unsigned long long v) -> decimal32
constexpr auto operator "" _DD(const char* str) -> decimal64
constexpr auto operator "" _dd(const char* str) -> decimal64
constexpr auto operator "" _DD(unsigned long long v) -> decimal64
constexpr auto operator "" _dd(unsigned long long v) -> decimal64
constexpr auto operator "" _DL(const char* str) -> decimal128
constexpr auto operator "" _dl(const char* str) -> decimal128
constexpr auto operator "" _DL(unsigned long long v) -> decimal128
constexpr auto operator "" _dl(unsigned long long v) -> decimal128
} //namespace decimal
} //namespace boost
Important
|
0.2_DF is equivalent to calling decimal32{2, -1} .
These are different from calling decimal32{0.2} which converts from a floating-point value which may or may not be exactly represented.
|
Numeric Constants
Overview
Contains all constants provided by C++20’s <numbers>
specialized for the decimal types. These do not require C++20.
-
e_v - Euler’s Number
-
log2e_v - log2(e)
-
log10e_v - log10(e)
-
pi_v - pi
-
inv_pi_v - 1/pi
-
inv_sqrtpi_v - 1 / sqrt(pi)
-
ln2_v - ln(2)
-
ln10_v - ln(10)
-
sqrt2_v - sqrt(2)
-
sqrt3_v - sqrt(3)
-
sqrt10_v - sqrt(10)
-
inv_sqrt3_v - 1 / sqrt(3)
-
cbrt2_v - cbrt(2)
-
cbrt10_v - cbrt(10)
-
egamma_v - Euler–Mascheroni constant
-
phi_v - The Golden Ratio
There are also non-template variables that provide the constant as a decimal64 type.
Reference
#include <boost/decimal/numbers.hpp>
namespace boost {
namespace decimal {
template <typename Decimal>
static constexpr Decimal e_v;
template <typename Decimal>
static constexpr Decimal log2e_v;
template <typename Decimal>
static constexpr Decimal log10e_v;
template <typename Decimal>
static constexpr Decimal pi_v;
template <typename Decimal>
static constexpr Decimal inv_pi_v;
template <typename Decimal>
static constexpr Decimal inv_sqrtpi_v;
template <typename Decimal>
static constexpr Decimal ln2_v;
template <typename Decimal>
static constexpr Decimal ln10_v;
template <typename Decimal>
static constexpr Decimal sqrt2_v;
template <typename Decimal>
static constexpr Decimal sqrt3_v;
template <typename Decimal>
static constexpr Decimal sqrt10_v;
template <typename Decimal>
static constexpr Decimal inv_sqrt2_v;
template <typename Decimal>
static constexpr Decimal inv_sqrt3_v;
template <typename Decimal>
static constexpr Decimal cbrt2_v;
template <typename Decimal>
static constexpr Decimal cbrt10_v;
template <typename Decimal>
static constexpr Decimal egamma_v;
template <typename Decimal>
static constexpr Decimal phi_v;
static constexpr auto e {e_v<decimal64>};
static constexpr auto log2e {log2e_v<decimal64>};
static constexpr auto log10e {log10e_v<decimal64>};
static constexpr auto pi {pi_v<decimal64>};
static constexpr auto inv_pi {inv_pi_v<decimal64>};
static constexpr auto inv_sqrtpi {inv_sqrtpi_v<decimal64>};
static constexpr auto ln2 {ln2_v<decimal64>};
static constexpr auto ln10 {ln10_v<decimal64>};
static constexpr auto sqrt2 {sqrt2_v<decimal64>};
static constexpr auto sqrt3 {sqrt3_v<decimal64>};
static constexpr auto sqrt10 {sqrt10_v<decimal64>};
static constexpr auto inv_sqrt2 {inv_sqrt2_v<decimal64>};
static constexpr auto inv_sqrt3 {inv_sqrt3_v<decimal64>};
static constexpr auto cbrt2 {cbrt2_v<decimal64>};
static constexpr auto cbrt10 {cbrt10_v<decimal64>};
static constexpr auto egamma {egamma_v<decimal64>};
static constexpr auto phi {phi_v<decimal64>};
} //namespace decimal
} //namespace boost
cmath support
<cmath>
Decimal contains overloads for all functions from <cmath>
, and they have the same handling as built-in floating point types.
They are also all constexpr with C++14 unlike the built-in floating point types which require either C++23 or 26.
Nearest integer floating point operations
Floating point manipulation functions
Classification and comparison
Summary
namespace boost {
namespace decimal {
template <typename DecimalType>
constexpr DecimalType abs(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType fabs(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType abs(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType fmod(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr DecimalType remainder(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr DecimalType remquo(DecimalType x, DecimalType y, int* quo) noexcept;
template <typename DecimalType>
constexpr DecimalType fma(DecimalType x, DecimalType y, DecimalType z) noexcept;
template <typename DecimalType>
constexpr DecimalType fmax(DecimalType x, DecimalType y, int* quo) noexcept;
template <typename DecimalType>
constexpr DecimalType fmin(DecimalType x, DecimalType y, int* quo) noexcept;
template <typename DecimalType>
constexpr DecimalType fdim(DecimalType x, DecimalType y, int* quo) noexcept;
constexpr decimal32 nand32(const char* arg) noexcept;
constexpr decimal64 nand64(const char* arg) noexcept;
constexpr decimal128 nand128(const char* arg) noexcept;
template <typename DecimalType>
constexpr DecimalType exp(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType exp2(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType expm1(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType log(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType log10(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType log2(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType log1p(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType pow(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr DecimalType sqrt(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType cbrt(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType hypot(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr DecimalType hypot(DecimalType x, DecimalType y, DecimalType z) noexcept;
template <typename DecimalType>
constexpr DecimalType sin(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType cos(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType tan(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType asin(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType acos(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType atan(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType atan2(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr DecimalType sinh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType cosh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType tanh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType asinh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType acosh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType atanh(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType erf(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType erfc(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType tgamma(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType lgamma(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType ceil(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType floor(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType trunc(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType round(DecimalType x) noexcept;
template <typename DecimalType>
constexpr long lround(DecimalType x) noexcept;
template <typename DecimalType>
constexpr long long llround(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType nearbyint(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType rint(DecimalType x) noexcept;
template <typename DecimalType>
constexpr long lrint(DecimalType x) noexcept;
template <typename DecimalType>
constexpr long long llrint(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType frexp(DecimalType x, int* exp) noexcept;
template <typename DecimalType>
constexpr DecimalType ldexp(DecimalType x, int exp) noexcept;
template <typename DecimalType>
constexpr DecimalType modf(DecimalType x, DecimalType* iptr) noexcept;
template <typename DecimalType>
constexpr DecimalType scalbn(DecimalType x, int exp) noexcept;
template <typename DecimalType>
constexpr DecimalType scalbln(DecimalType x, long exp) noexcept;
template <typename DecimalType>
constexpr int ilogb(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType logb(DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType nextafter(DecimalType from, DecimalType to) noexcept;
template <typename DecimalType>
constexpr DecimalType nexttoward(DecimalType from, long double to) noexcept;
template <typename DecimalType>
constexpr DecimalType copysign(DecimalType mag, DecimalType sgn) noexcept;
template <typename DecimalType>
constexpr int fpclassify(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool isfinite(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool isinf(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool isnan(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool isnormal(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool signbit(DecimalType x) noexcept;
template <typename DecimalType>
constexpr bool isgreater(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr bool isgreaterequal(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr bool isless(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr bool islessgreater(DecimalType x, DecimalType y) noexcept;
template <typename DecimalType>
constexpr bool isunordered(DecimalType x, DecimalType y) noexcept;
} //namespace decimal
} //namespace boost
C++17 Mathematical Special Functions
The following functions have been implemented for decimal types:
Summary
namespace boost {
namespace decimal {
template <typename DecimalType>
constexpr DecimalType assoc_laguerre(unsigned int n, unsigned int m, DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType hermite(unsigned int n, DecimalType x) noexcept;
template <typename DecimalType>
constexpr DecimalType laguerre(unsigned int n, DecimalType x) noexcept;
} //namespace decimal
} //namespace boost
Non-standard Functions
The following are convenience functions, or are prescribed in IEEE 754-2019 as required for decimal floating point types.
issignaling
template <typename Decimal>
constexpr bool issignaling(Decimal x) noexcept;
Effects: If x is an sNaN returns true, otherwise returns false.
samequantum
template <typename Decimal>
constexpr bool samequantum(Decimal x, Decimal y) noexcept;
constexpr bool samequantumd32(decimal32 x, decimal32 y) noexcept;
constexpr bool samequantumd64(decimal64 x, decimal64 y) noexcept;
constexpr bool samequantumd128(decimal128 x, decimal128 y) noexcept;
Effects: Determines if the quantum (unbiased) exponents of x and y are the same.
If both x and y are NaN, or infinity, they have the same quantum exponents.
If exactly one operand is infinity or exactly one operand is NaN, they do not have the same quantum exponents.
quantexp
template <typename Decimal>
constexpr int quantexp(Decimal x) noexcept;
constexpr bool quantexp32(decimal32 x) noexcept;
constexpr bool quantexp64(decimal64 x) noexcept;
constexpr bool quantexp128(decimal128 x) noexcept;
Effects: if x is finite, returns its quantum exponent.
Otherwise, a domain error occurs and INT_MIN
is returned.
quantized
template <typename Decimal>
constexpr Decimal quantized(Decimal x, Decimal y) noexcept;
constexpr decimal32 quantized32(decimal32 x, decimal32 y) noexcept;
constexpr decimal64 quantized64(decimal64 x, decimal64 y) noexcept;
constexpr decimal128 quantized128(decimal128 x, decimal128 y) noexcept;
Returns: a number that is equal in value (except for any rounding) and sign to x, and which has an exponent set to be equal to the exponent of y.
If the exponent is being increased, the value is correctly rounded according to the current rounding mode;
If the result does not have the same value as x, the "inexact" floating-point exception is raised.
If the exponent is being decreased and the significand of the result has more digits than the type would allow, the "invalid" floating-point exception is raised and the result is sNaN.
If one or both operands are NaN the result is sNaN.
Otherwise, if only one operand is infinity, the "invalid" floating-point exception is raised and the result is sNaN.
If both operands are infinity, the result is infinity, with the same sign as x.
The quantize functions do not signal underflow.
frexp10
template <typename Decimal>
constexpr auto frexp10(Decimal num, int* expptr) noexcept;
constexpr std::uint32_t frexpd32(decimal32 num, int* expptr) noexcept;
constexpr std::uint64_t frexpd64(decimal64 num, int* expptr) noexcept;
constexpr boost::decimal::detail::uint128 frexpd128(decimal128 num, int* expptr) noexcept;
This function is very similar to frexp, but returns the significand and an integral power of 10 since the FLT_RADIX
of this type is 10.
The significand is normalized to the number of digits of precision the type has (e.g. for decimal32 it is [1'000'000, 9'999'999]).
trunc_to
template <typename Decimal>
constexpr Decimal trunc_to(Decimal val, int precision = 0);
The function returns the decimal type with number of fractional digits equal to the value of precision.
trunc_to
is similar to trunc, and with the default precision argument of 0 it is identical.
cstdlib support
<cstdlib>
The following functions analogous to those from <cstdlib> are provided:
namespace boost {
namespace decimal {
inline decimal64 strtod(const char* str, char** endptr) noexcept;
inline decimal64 wcstod(const wchar_t* str, wchar_t** endptr) noexcept;
inline decimal32 strtod32(const char* str, char** endptr) noexcept;
inline decimal32 wcstod32(const wchar_t* str, wchar_t** endptr) noexcept;
inline decimal64 strtod64(const char* str, char** endptr) noexcept;
inline decimal64 wcstod64(const wchar_t* str, wchar_t** endptr) noexcept;
inline decimal128 strtod128(const char* str, char** endptr) noexcept;
inline decimal128 wcstod128(const wchar_t* str, wchar_t** endptr) noexcept;
} //namespace decimal
} //namespace boost
charconv support
<charconv>
The following functions analogous to those from <charconv> are provided:
Important
|
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 .
|
namespace boost {
namespace decimal {
enum class chars_format : unsigned
{
scientific = 1 << 0,
fixed = 1 << 1,
hex = 1 << 2,
general = fixed | scientific
};
struct from_chars_result
{
const char* ptr;
std::errc ptr;
friend constexpr auto operator==(const from_chars_result& lhs, const from_chars_result& rhs) noexcept = default;
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
}
struct to_chars_result
{
char* ptr;
std::errc ptr;
friend constexpr auto operator==(const to_chars_result& lhs, const to_chars_result& rhs) noexcept = default;
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
}
template <typename DecimalType>
constexpr from_chars_result from_chars(const char* first, const char* last, DecimalType& value, chars_format fmt = chars_format::general)
template <typename DecimalType>
BOOST_DECIMAL_CONSTEXPR to_chars_result to_chars(char* first, char* last, DecimalType value) noexcept;
template <typename DecimalType>
BOOST_DECIMAL_CONSTEXPR to_chars_result to_chars(char* first, char* last, DecimalType value, chars_format fmt) noexcept;
template <typename DecimalType>
BOOST_DECIMAL_CONSTEXPR to_chars_result to_chars(char* first, char* last, DecimalType value, chars_format fmt, int precision) noexcept;
} //namespace decimal
} //namespace boost
Note
|
BOOST_DECIMAL_CONSTEXPR is defined if:
|
-
_MSC_FULL_VER
>= 192528326 -
__GNUC__
>= 9 -
Compiler has:
__builtin_is_constant_evaluated()
-
C++20 support with:
std::is_constant_evaluated()
The library offers an additional feature for sizing buffers without specified precision and in general format
namespace boost {
namespace decimal {
template <typename T>
struct limits
{
static constexpr int max_chars;
}
} //namespace decimal
} //namespace boost
The member can then be used to size buffers such as:
#include <boost/decimal.hpp>
#include <iostream>
int main()
{
using namespace boost::decimal;
decimal32 val {5, -1};
char buffer[limits<T>::max_chars];
auto r_to = to_chars(buffer, buffer + sizeof(buffer), val);
*r_to.ptr = '\0';
std::cout << buffer << std::endl;
return 0;
}
cfenv support
<cfenv>
As opposed to binary floating point types IEEE 754 defined 5 rounding modes instead of 4. They are:
-
Downward
-
To nearest
-
To nearest from zero
-
Toward zero
-
Upward
The default rounding mode is to nearest from zero.
Important
|
The rounding mode can only be changed at runtime. All constexpr calculations will use the default of to nearest from zero. |
namespace boost {
namespace decimal {
enum class rounding_mode : unsigned
{
fe_dec_downward = 1 << 0,
fe_dec_to_nearest = 1 << 1,
fe_dec_to_nearest_from_zero = 1 << 2,
fe_dec_toward_zero = 1 << 3,
fe_dec_upward = 1 << 4,
fe_dec_default = fe_dec_to_nearest_from_zero
};
rounding_mode fegetround() noexcept;
rounding_mode fesetround(rounding_mode round) noexcept;
} //namespace decimal
} //namespace boost
cfloat support
<cfloat>
The following macros analogous to those from <cfloat> for the decimal floating point types:
// Number of digits in the coefficient
#define BOOST_DECIMAL_DEC32_MANT_DIG 7
#define BOOST_DECIMAL_DEC64_MANT_DIG 16
#define BOOST_DECIMAL_DEC128_MANT_DIG 34
// Minimum exponent
#define BOOST_DECIMAL_DEC32_MIN_EXP -94
#define BOOST_DECIMAL_DEC64_MIN_EXP -382
#define BOOST_DECIMAL_DEC128_MIN_EXP -6142
// Maximum exponent
#define BOOST_DECIMAL_DEC32_MAX_EXP 97
#define BOOST_DECIMAL_DEC64_MAX_EXP 385
#define BOOST_DECIMAL_DEC128_MAX_EXP 6145
// Maximum Finite Value
#define BOOST_DECIMAL_DEC32_MAX std::numeric_limits<boost::decimal::decimal32>::max()
#define BOOST_DECIMAL_DEC64_MAX std::numeric_limits<boost::decimal::decimal64>::max()
#define BOOST_DECIMAL_DEC128_MAX std::numeric_limits<boost::decimal::decimal128>::max()
// Minimum positive normal vlaue
#define BOOST_DECIMAL_DEC32_MIN std::numeric_limits<boost::decimal::decimal32>::min()
#define BOOST_DECIMAL_DEC64_MIN std::numeric_limits<boost::decimal::decimal64>::min()
#define BOOST_DECIMAL_DEC128_MIN std::numeric_limits<boost::decimal::decimal128>::min()
// Minimum positive sub-normal value
#define BOOST_DECIMAL_DEC32_MAX std::numeric_limits<boost::decimal::decimal32>::denorm_min()
#define BOOST_DECIMAL_DEC64_MAX std::numeric_limits<boost::decimal::decimal64>::denorm_min()
#define BOOST_DECIMAL_DEC128_MAX std::numeric_limits<boost::decimal::decimal128>::denorm_min()
Additionally BOOST_DECIMAL_DEC_EVAL_METHOD
is similar to FLT_EVAL_METHOD
: https://en.cppreference.com/w/cpp/types/climits/FLT_EVAL_METHOD
The valid values are:
-
0: all operations evaluated in the range and precision of the native type
-
1: all
decimal32
operations are evaluated with the precision and range ofdecimal64
internally, and returned asdecimal64
-
2: all
decimal32
anddecimal64
operations are evaluated with the precision and range ofdecimal128
internally, and returned as the original type.
To use the functionallity you must #define BOOST_DECIMAL_DEC_EVAL_METHOD
to the value you want before you #include <boost/decimal.hpp>
.
cstdio support
<cstdio>
The following functions analogous to those from <cstdio> are provided:
namespace boost {
namespace decimal {
template <typename... Dec>
int snprintf(char* buffer, std::size_t buf_size, const char* format, Dec... value) noexcept;
template <typename... Dec>
int sprintf(char* buffer, const char* format, Dec... value) noexcept;
template <typename... Dec>
int fprintf(std::FILE* buffer, const char* format, Dec... values) noexcept;
template <typename... Dec>
int printf(const char* format, Dec... values) noexcept;
} //namespace decimal
} //namespace boost
Warning
|
In the interest of safety sprintf simply calls snprintf with buf_size equal to sizeof(buffer).
|
Type Modifiers
The type modifiers to be used are:
-
"H" for
decimal32
-
"D" for
decimal64
-
"DD" for
decimal128
The remaining specifications of cstdio are the same as usual:
-
"g" or "G" for general format
-
"e" or "E" for scientific format
-
"f" for fixed format
-
"a" or "A" for hex format
Note
|
The uppercase format will return with all applicable values in uppercase (e.g. 3.14E+02 vs 3.14e+02) |
Examples
Here are some example formats:
-
"%Hg" will print a
decimal32
in general format -
"%.3De" will print a
decimal64
in scientific format with 3 digits of precision -
"%.5DDA" will print a
decimal128
in hex format with 5 digits of precision and all letters will be capitalized (e.g. 1.F2CP+2 vs 1.f2cp+2)
functional support
<functional>
The following functions from <functional> are overloaded:
namespace std {
template <>
struct hash<boost::decimal::decimal32>;
template <>
struct hash<boost::decimal::decimal64>;
template <>
struct hash<boost::decimal::decimal128>;
} //namespace std
limits support
<limits>
The following from <limits> are overloaded for each type with associated values for reference:
namespace std {
template <>
#ifdef _MSC_VER
class numeric_limits<boost::decimal::decimal32>
#else
struct numeric_limits<boost::decimal::decimal32>
#endif
{
#ifdef _MSC_VER
public:
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true;
// These members were deprecated in C++23
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true;
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 7;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -95;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 96;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits<std::uint32_t>::traps;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true;
// Member functions
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 min()
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 max();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 lowest();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 epsilon();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 round_error();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 infinity();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 quiet_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 signaling_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal32 denorm_min();
};
template <>
#ifdef _MSC_VER
class numeric_limits<boost::decimal::decimal64>
#else
struct numeric_limits<boost::decimal::decimal64>
#endif
{
#ifdef _MSC_VER
public:
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true;
// These members were deprecated in C++23
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true;
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 16;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -382;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 385;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits<std::uint64_t>::traps;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true;
// Member functions
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 min()
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 max();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 lowest();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 epsilon();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 round_error();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 infinity();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 quiet_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 signaling_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal64 denorm_min();
};
template<>
#ifdef _MSC_VER
class numeric_limits<boost::decimal::decimal128>
#else
struct numeric_limits<boost::decimal::decimal128>
#endif
{
#ifdef _MSC_VER
public:
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true;
// These members were deprecated in C++23
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true;
#endif
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 34;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -6142;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 6145;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits<std::uint64_t>::traps;
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true;
// Member functions
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 min()
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 max();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 lowest();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 epsilon();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 round_error();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 infinity();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 quiet_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 signaling_NaN();
BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr boost::decimal::decimal128 denorm_min();
};
} // Namespace std
Configuration Macros
The following configuration macros are available:
-
BOOST_DECIMAL_DISABLE_CASSERT
: Disables the use of<cassert>
and all run-time assertions. -
BOOST_DECIMAL_DISABLE_IOSTREAM
: Disables the use of I/O streaming and removes all associated headers (e.g.<iostream>
,<iosfwd>
,<cwchar>
, etc.) -
BOOST_DECIMAL_DISABLE_CLIB
: Defines both of the above macros. In testing this reduces ROM usage by ~50%. -
BOOST_DECIMAL_ALLOW_IMPLICIT_CONVERSIONS
: Allows a binary floating-point type (e.g.double
) to be implicitly converted to a decimal floating point type. This option is not recommended, but can be useful if you want to use specific functionality from the standard library with internal conversions such as:
constexpr decimal64 half {5, -1};
std::complex<decimal64> test_val {half, half};
const auto res = std::acos(test_val);
-
BOOST_DECIMAL_FAST_MATH
performs optimizations similar to that of the-ffast-math
compiler flag such as removing all checks for non-finite values. This flag increases the performance of the basis operations (e.g. add, sub, mul, div, and comparisons) by up to 20%.
type_traits support
<type_traits>
The following type traits from boost.type_traits have been overloaded for the decimal types:
namespace boost {
template <> struct is_arithmetic<boost::decimal::decimal32> : public true_type {};
template <> struct is_arithmetic<boost::decimal::decimal64> : public true_type {};
template <> struct is_arithmetic<boost::decimal::decimal128> : public true_type {};
template <> struct is_fundamental<boost::decimal::decimal32> : public true_type {};
template <> struct is_fundamental<boost::decimal::decimal64> : public true_type {};
template <> struct is_fundamental<boost::decimal::decimal128> : public true_type {};
template <> struct is_scalar<boost::decimal::decimal32> : public true_type {};
template <> struct is_scalar<boost::decimal::decimal64> : public true_type {};
template <> struct is_scalar<boost::decimal::decimal128> : public true_type {};
template <> struct is_class<boost::decimal::decimal32> : public false_type {};
template <> struct is_class<boost::decimal::decimal64> : public false_type {};
template <> struct is_class<boost::decimal::decimal128> : public false_type {};
template <> struct is_pod<boost::decimal::decimal32> : public true_type {};
template <> struct is_pod<boost::decimal::decimal64> : public true_type {};
template <> struct is_pod<boost::decimal::decimal128> : public true_type {};
} // namespace boost
Caution
|
These overloads only exist if <boost/type_traits.hpp> is installed. In a standalone environment they do not exist. |
An additional type_trait has been added is_decimal_floating_point
, which is defined as follows:
namespace boost {
template <typename T> struct is_decimal_floating_point : public false_type{};
template <typename T> struct is_decimal_floating_point<const T> : public is_decimal_floating_point<T>{};
template <typename T> struct is_decimal_floating_point<volatile const T> : public is_decimal_floating_point<T>{};
template <typename T> struct is_decimal_floating_point<volatile T> : public is_decimal_floating_point<T>{};
template <> struct is_decimal_floating_point<boost::decimal::decimal32> : public true_type{};
template <> struct is_decimal_floating_point<boost::decimal::decimal64> : public true_type{};
template <> struct is_decimal_floating_point<boost::decimal::decimal128> : public true_type{};
} // namespace boost
Examples
All examples can be found in the library examples/
folder as well.
Construction from an Integer and Exponent
#include <boost/decimal.hpp>
#include <iostream>
int main()
{
constexpr boost::decimal::decimal32 a {2, -1}; // Constructs the number 0.2
constexpr boost::decimal::decimal32 b {1, -1}; // Constructs the number 0.1
boost::decimal::decimal32 sum {a + b};
std::cout << sum << std::endl; // prints 0.3
const boost::decimal::decimal32 neg_a {2, -1, true}; // Constructs the number -0.2
sum += neg_a;
std::cout << sum << std::endl; // Prints 0.1
return 0;
}
This is the recommended way of constructing a fractional number as opposed to decimal32 a {0.2}
.
The representation is exact with integers whereas you may get surprising or unwanted conversion from binary floating point
Promotion
#include <boost/decimal.hpp>
#include <type_traits>
#include <cassert>
int main()
{
using namespace boost::decimal;
decimal32 x {1}; // Constructs from an integer
decimal64 y {2};
auto sum {x + y};
assert(std::is_same<decimal64, decltype(sum)>::value);
return 0;
}
charconv
#include <boost/decimal.hpp>
#include <iostream>
#include <cassert>
int main()
{
using namespace boost::decimal;
decimal64 val {0.25}; // Construction from a double (not recommended but explicit construction is allowed)
char buffer[256];
auto r_to = to_chars(buffer, buffer + sizeof(buffer) - 1, val);
assert(r_to); // checks std::errc()
*r_to.ptr = '\0';
decimal64 return_value;
auto r_from = from_chars(buffer, buffer + std::strlen(buffer), return_value);
assert(r_from);
assert(val == return_value);
std::cout << " Initial Value: " << val << '\n'
<< "Returned Value: " << return_value << std::endl;
return 0;
}
Rounding Mode
#include <boost/decimal.hpp>
#include <cassert>
int main()
{
auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest_from_zero
auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_to_nearest);
assert(default_rounding_mode != new_rounding_mode);
return 0;
}
Generic Programming
#include <boost/decimal.hpp>
#include <limits>
#include <cmath>
int error_counter = 0;
template <typename T>
bool float_equal(T lhs, T rhs)
{
using std::fabs;
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
}
template <typename T>
void test(T val)
{
using std::sin; // ADL allows builtin and decimal types to both be used
if (!float_equal(sin(val), -sin(-val))) // sin(x) == -sin(-x)
{
++error_counter;
}
}
int main()
{
test(-0.5F);
test(-0.5);
test(-0.5L);
test(boost::decimal::decimal32{5, -1, true});
test(boost::decimal::decimal64{5, -1, true});
test(boost::decimal::decimal128{5, -1, true});
return error_counter;
}
Literals and Constants
#include <boost/decimal.hpp>
#include <cassert>
template <typename T>
bool float_equal(T lhs, T rhs)
{
using std::fabs;
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
}
int main()
{
using namespace boost::decimal;
const auto pi_32 {"3.141592653589793238"_DF};
const auto pi_64 {"3.141592653589793238"_DD};
assert(float_equal(pi_32, static_cast<decimal32>(pi_64))); // Explicit conversion between decimal types
assert(float_equal(pi_32, boost::decimal::numbers::pi_v<decimal32>)); // Constants available in numbers namespace
assert(float_equal(pi_64, numbers::pi)); // Default constant type is decimal64
return 0;
}
Benchmarks
This section describes a range of performance benchmarks that have been run comparing this library with the standard library, and how to run your own benchmarks if required.
The values in the ratio column are how many times longer running a specific operation takes in comparison to the same operation with a double
.
Important
|
On nearly all platforms there is hardware support for binary floating point math, so we are comparing hardware to software runtimes; Decimal will be slower |
How to run the Benchmarks
To run the benchmarks yourself, navigate to the test folder and define BOOST_DECIMAL_RUN_BENCHMARKS
when running the tests.
An example on Linux with b2: ../../../b2 cxxstd=20 toolset=gcc-13 define=BOOST_DECIMAL_RUN_BENCHMARKS benchmarks -a release
.
Comparisons
The benchmark for comparisons generates a random vector containing 2,000,000 elements and does operations >
, >=
, <
, <=
, ==
, and !=
between vec[i] and vec[i + 1]
.
This is repeated 5 times to generate stable results.
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
8587 |
1.376 |
|
6240 |
1.000 |
|
275,597 |
44.166 |
|
296,929 |
47.587 |
|
821,847 |
131.706 |
|
99,664 |
15.972 |
|
102,132 |
16.367 |
|
146,302 |
23.446 |
Basic Operations
The benchmark for these operations generates a random vector containing 2,000,000 elements and does operations +
, -
, *
, /
between vec[i] and vec[i + 1]
.
This is repeated 5 times to generate stable results.
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Addition
Type |
Runtime (us) |
Ratio to |
|
2705 |
0.859 |
|
3148 |
1.000 |
|
351,505 |
111.660 |
|
359,425 |
114.176 |
|
1,446,674 |
459.553 |
|
146,873 |
46.656 |
|
139,294 |
44.248 |
|
707,308 |
224.685 |
Subtraction
Type |
Runtime (us) |
Ratio to |
|
3339 |
2.014 |
|
1658 |
1.000 |
|
267,646 |
161.427 |
|
303,589 |
183.106 |
|
954,211 |
575.519 |
|
147,112 |
88.729 |
|
145,606 |
87.820 |
|
394,538 |
2387.960 |
Multiplication
Type |
Runtime (us) |
Ratio to |
|
1646 |
0.957 |
|
1720 |
1.000 |
|
313,219 |
182.104 |
|
583,818 |
339.429 |
|
1,881,936 |
1094.149 |
|
86,093 |
50.054 |
|
333,582 |
193.943 |
|
1,269,429 |
738.040 |
Division
Type |
Runtime (us) |
Ratio to |
|
2120 |
0.547 |
|
3874 |
1.000 |
|
307,337 |
79.333 |
|
447,910 |
115.620 |
|
2,544,798 |
656.892 |
|
105,796 |
27.309 |
|
291,671 |
75.289 |
|
302,003 |
77.956 |
Selected Special Functions
The benchmark for these operations generates a random vector containing 2,000,000 elements and does operations +
, -
, *
, /
between vec[i] and vec[i + 1]
.
This is repeated 5 times to generate stable results.
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
SQRT
Type |
Runtime (us) |
Ratio to |
|
2021 |
0.626 |
|
3229 |
1.000 |
|
4,826,066 |
1494.601 |
|
7,780,637 |
2409.612 |
|
100,269,145 |
31052.693 |
<charconv>
For all the following the results compare against boost.charconv for 10'000'000 conversions.
from_chars
general
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
235,816 |
0.953 |
|
247,307 |
1.000 |
|
366,682 |
1.483 |
|
485,965 |
1.965 |
Note
|
decimal128 is currently absent due to results showing it is 2 orders of magnitude faster than the others.
This should not be the case so will be investigated.
|
from_chars
scientific
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
241,893 |
0.975 |
|
247,975 |
1.000 |
|
358,189 |
1.444 |
|
477,574 |
1.926 |
Note
|
decimal128 is currently absent due to results showing it is 2 orders of magnitude faster than the others.
This should not be the case so will be investigated.
|
to_chars
general shortest representation
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
316,300 |
1.040 |
|
304,272 |
1.000 |
|
406,053 |
1.335 |
|
678,451 |
2.230 |
|
6,309,346 |
20.736 |
to_chars
general 6-digits of precision
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
323,867 |
0.967 |
|
334,989 |
1.000 |
|
409,608 |
1.223 |
|
702,339 |
2.097 |
|
6,305,521 |
18.823 |
to_chars
scientific shortest representation
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
286,330 |
1.011 |
|
283,287 |
1.000 |
|
290,117 |
1.024 |
|
499,637 |
1.764 |
|
3,096,910 |
10.932 |
to_chars
scientific 6-digits of precision
M1 macOS Results
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4
Type |
Runtime (us) |
Ratio to |
|
258,710 |
0.809 |
|
319,676 |
1.000 |
|
292,250 |
0.914 |
|
516,399 |
1.615 |
|
3,108,380 |
9.724 |
Design Decisions
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.
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.
References
The following books, papers and blog posts serve as the basis for the algorithms used in the library:
-
Michael F. Cowlishaw Decimal Floating-Point: Algorism(sic) for Computers, Proceedings of the 16th IEEE Symposium on Computer Arithmetic, 2003
-
Donald E. Knuth, The Art of Computer Programming Volume 2 Seminumerical Algorithms, 3rd edition, 1998
-
Jean-Michel Muller, Elementary Functions, 3rd edition, 2010
-
Jean-Michel Muller, et. al., Handbook of Floating-Point Arithmetic, 2000
-
John F. Hart, et. al., Computer Approximations, 1968
-
IEEE, IEEE Standard for Floating-Point Arithmetic, 2019
Copyright and License
This documentation is copyright 2023 Matt Borland and is distributed under the Boost Software License, Version 1.0.