Fast Types
Now that we have seen the three basic types as specified in IEEE-754 there are three additional adjacent types: decimal_fast32_t, decimal_fast64_t, and decimal_fast128_t.
These types yield similar computational results but with faster performance.
These types make the classic tradeoff of space for time as they require more storage width than the IEEE 754 conformant type.
For example decimal32_t is fundamentally:
class decimal32_t final
{
std::uint32_t bits_;
};
Where the value of decimal32_t is encoded into bits_ per IEEE 754 BID encoding pattern.
Conversely, the internals of decimal_fast32_t are:
class decimal_fast32_t final
{
std::uint32_t signficand_;
std::uint8_t exponent_;
bool sign_;
};
Instead of having to decode the bits at every operation, decimal_fast32_t is able to skip this step.
The other major difference is that decimal32_t has cohorts as previously discussed.
This requires normalization to ensure correct results for the comparison:
decimal32_t a {20};
decimal32_t b {2, 1};
assert(a == b);
In this example a and b both have different encodings (cohorts), so they must be normalized before comparison.
decimal_fast32_t and the other fast types store the significand and exponent in a normalized fashion, so once again we can skip that step for every operation.
For behavior of the individual operators see the reference section for the IEEE decimal types.
Limitations
-
These types are not uniformly faster for all operations. One known corner case is
to_charswith the shortest precision. Since these types are normalized they will normally have a number of trailing zeros to accomplish the normalization. In the case where we want to output the shortest precision we must identify and remove these trailing zeros. -
With these types no cohort information exists, as the type stores its value in a normalized fashion.
-
Because the information is stored normalized these types do not support sub-normal values. Any value that would be a sub-normal is flushed to 0 instead.
-
Overview
// Paragraph numbers are from ISO/IEC DTR 24733
namespace boost {
namespace decimal {
class decimal_fast32_t;
class decimal_fast64_t;
class decimal_fast128_t;
// 3.2.7 unary arithmetic operators:
constexpr decimal_fast32_t operator+(decimal_fast32_t rhs) noexcept;
constexpr decimal_fast64_t operator+(decimal_fast64_t rhs) noexcept;
constexpr decimal_fast128_t operator+(decimal_fast128_t rhs) noexcept;
constexpr decimal_fast32_t operator-(decimal_fast32_t rhs) noexcept;
constexpr decimal_fast64_t operator-(decimal_fast64_t rhs) noexcept;
constexpr decimal_fast128_t operator-(decimal_fast128_t 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, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast128_t lhs, RHS rhs) noexcept;
// 3.2.9 comparison operators:
// LHS and RHS can be any integer or decimal type
constexpr bool operator==(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator==(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator!=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator<(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator<=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator>(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator>=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal_fast128_t lhs, RHS rhs) noexcept;
// If C++20 is available
// LHS and RHS can be any integer or decimal type
constexpr std::partial_ordering operator<=>(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr std::partial_ordering operator<=>(LHS lhs, decimal_fast128_t 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,
decimal_fast32_t& d);
template <typename charT, typename traits>
std::basic_istream<charT, traits>&
operator>>(std::basic_istream<charT, traits> & is,
decimal_fast64_t& d);
template <typename charT, typename traits>
std::basic_istream<charT, traits>&
operator>>(std::basic_istream<charT, traits> & is,
decimal_fast128_t& 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,
decimal_fast32_t d);
template <typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
decimal_fast64_t d);
template <typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
decimal_fast128_t 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. decimal_fast32_t * uint64_t → decimal_fast32_t)
In the event of binary arithmetic between two decimal types the result will be the higher precision type of the two (e.g. decimal64_t + decimal_fast32_t → decimal64_t). If the binary arithmetic is between an IEEE type, and it’s fast equivalent, the result is the fast type (e.g. decimal_fast128_t - decimal128_t → decimal_fast128_t).
Operator Behaviors
Unary Plus
// 3.2.7 unary arithmetic operators:
constexpr decimal_fast32_t operator+(decimal_fast32_t rhs) noexcept;
constexpr decimal_fast64_t operator+(decimal_fast64_t rhs) noexcept;
constexpr decimal_fast128_t operator+(decimal_fast128_t rhs) noexcept;
Returns rhs unmodified in all cases.
Unary Minus
constexpr decimal_fast32_t operator-(decimal_fast32_t rhs) noexcept;
constexpr decimal_fast64_t operator-(decimal_fast64_t rhs) noexcept;
constexpr decimal_fast128_t operator-(decimal_fast128_t rhs) noexcept;
Returns negated rhs in all cases.
Addition
// 3.2.8 binary arithmetic operators:
// LHS and RHS can be any integer or decimal type
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator+(decimal_fast128_t lhs, RHS rhs) noexcept;
Returns the result of lhs + rhs subject to:
-
If either
lhsorrhsareNAN, returnsQNAN -
If
lhsandrhsareINFof opposite sign, returnsQNAN -
If either
lhsorrhsareINF, returnsINFof same sign -
If
lhs + rhsoverflows, returnsINF -
If
lhs + rhsunderflows, returns0
Subtraction
// 3.2.8 binary arithmetic operators:
// LHS and RHS can be any integer or decimal type
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator-(decimal_fast128_t lhs, RHS rhs) noexcept;
Returns the result of lhs - rhs subject to:
-
If either
lhsorrhsareNAN, returnsQNAN -
If
lhsandrhsareINFof opposite sign, returnsQNAN -
If either
lhsorrhsareINF, returnsINFof same sign -
If
lhs - rhsoverflows, returnsINF -
If
lhs - rhsunderflows, returns0
Multiplication
// 3.2.8 binary arithmetic operators:
// LHS and RHS can be any integer or decimal type
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator*(decimal_fast128_t lhs, RHS rhs) noexcept;
Returns the result of lhs * rhs subject to:
-
If either
lhsorrhsareNAN, returnsQNAN -
If either
lhsorrhsareINFand the other is0, returnsQNAN -
If either
lhsorrhsareINF, returnsINFof same sign -
If
lhs * rhsoverflows, returnsINF -
If
lhs * rhsunderflows, returns0
Division
// 3.2.8 binary arithmetic operators:
// LHS and RHS can be any integer or decimal type
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr /* see 3.2.8 */ operator/(decimal_fast128_t lhs, RHS rhs) noexcept;
Returns the result of lhs / rhs subject to:
-
If either
lhsorrhsareNAN, returnsQNAN -
If
lhsisINF:-
Returns
QNANifrhsisINF -
Returns
INFifrhsis notINF
-
-
If
rhsis0returnsINF -
If
rhsisINFreturns0 -
If
lhs / rhsoverflows, returnsINF -
If
lhs / rhsunderflows, returns0
Comparison Operators
// 3.2.9 comparison operators:
// LHS and RHS can be any integer or decimal type
constexpr bool operator==(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator==(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator==(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator==(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator!=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator!=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator!=(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator<(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator<(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator<(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator<=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator<=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator<=(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator>(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator>(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator>(decimal_fast128_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast32_t rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast64_t rhs) noexcept;
constexpr bool operator>=(LHS lhs, decimal_fast128_t rhs) noexcept;
constexpr bool operator>=(decimal_fast32_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal_fast64_t lhs, RHS rhs) noexcept;
constexpr bool operator>=(decimal_fast128_t lhs, RHS rhs) noexcept;
Returns the result of the comparison subject to:
-
If
lhsorrhsreturnsfalse-
This includes the case where both
lhsandrhsareNAN
-