// Copyright 2024 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_DECIMAL_DPD_CONVERSION_HPP #define BOOST_DECIMAL_DPD_CONVERSION_HPP #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include #endif namespace boost { namespace decimal { namespace detail { // See Table 3.4 constexpr auto encode_dpd(const std::uint8_t d1, const std::uint8_t d2, const std::uint8_t d3) -> std::uint16_t { constexpr std::uint8_t b3_mask {0b0001}; constexpr std::uint8_t b2_mask {0b0010}; constexpr std::uint8_t b1_mask {0b0100}; constexpr std::uint8_t b0_mask {0b1000}; const std::uint8_t b1[4] = { static_cast((d1 & b0_mask) >> 3U), static_cast((d1 & b1_mask) >> 2U), static_cast((d1 & b2_mask) >> 1U), static_cast((d1 & b3_mask)) }; BOOST_DECIMAL_ASSERT(b1[0] <= 1U && b1[1] <= 1U && b1[2] <= 1U && b1[3] <= 1); const std::uint8_t b2[4] = { static_cast((d2 & b0_mask) >> 3U), static_cast((d2 & b1_mask) >> 2U), static_cast((d2 & b2_mask) >> 1U), static_cast((d2 & b3_mask)) }; BOOST_DECIMAL_ASSERT(b2[0] <= 1U && b2[1] <= 1U && b2[2] <= 1U && b2[3] <= 1); const std::uint8_t b3[4] = { static_cast((d3 & b0_mask) >> 3U), static_cast((d3 & b1_mask) >> 2U), static_cast((d3 & b2_mask) >> 1U), static_cast((d3 & b3_mask)) }; BOOST_DECIMAL_ASSERT(b3[0] <= 1U && b3[1] <= 1U && b3[2] <= 1U && b3[3] <= 1); std::uint8_t result_b[10] {}; const auto table_val {(b1[0] << 2) + (b2[0] << 1) + b3[0]}; BOOST_DECIMAL_ASSERT(table_val >= 0b000 && table_val <= 0b111); // Now that we have dissected the bits of d1, d2, and d3 we can use the lookup table from 3.4 to generate // all possible combinations switch (table_val) { case 0b000: // b0, b1, b2 result_b[0] = b1[1]; result_b[1] = b1[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = b2[1]; result_b[4] = b2[2]; result_b[5] = b2[3]; // b6 result_b[6] = static_cast(0); // b7, b8, b9 result_b[7] = b3[1]; result_b[8] = b3[2]; result_b[9] = b3[3]; break; case 0b001: // b0, b1, b2 result_b[0] = b1[1]; result_b[1] = b1[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = b2[1]; result_b[4] = b2[2]; result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[9] = b3[3]; break; case 0b010: // b0, b1, b2 result_b[0] = b1[1]; result_b[1] = b1[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = b3[1]; result_b[4] = b3[2]; result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(0); result_b[8] = static_cast(1); result_b[9] = b3[3]; break; case 0b011: // b0, b1, b2 result_b[0] = b1[1]; result_b[1] = b1[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = static_cast(1); result_b[4] = static_cast(0); result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(1); result_b[8] = static_cast(1); result_b[9] = b3[3]; break; case 0b100: // b0, b1, b2 result_b[0] = b3[1]; result_b[1] = b3[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = b2[1]; result_b[4] = b2[2]; result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(1); result_b[8] = static_cast(0); result_b[9] = b3[3]; break; case 0b101: // b0, b1, b2 result_b[0] = b2[1]; result_b[1] = b2[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = static_cast(0); result_b[4] = static_cast(1); result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(1); result_b[8] = static_cast(1); result_b[9] = b3[3]; break; case 0b110: // b0, b1, b2 result_b[0] = b3[1]; result_b[1] = b3[2]; result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = static_cast(0); result_b[4] = static_cast(0); result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(1); result_b[8] = static_cast(1); result_b[9] = b3[3]; break; case 0b111: // b0, b1, b2 result_b[0] = static_cast(0); result_b[1] = static_cast(0); result_b[2] = b1[3]; // b3, b4, b5 result_b[3] = static_cast(1); result_b[4] = static_cast(1); result_b[5] = b2[3]; // b6 result_b[6] = static_cast(1); // b7, b8, b9 result_b[7] = static_cast(1); result_b[8] = static_cast(1); result_b[9] = b3[3]; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } // Now that we have the bit pattern of the result we need to write it into uint16_t and return the result std::uint16_t result {}; for (std::uint16_t i {}; i < 10U; ++i) { result |= static_cast(result_b[i] << (9 - i)); } return result; } constexpr auto decode_dpd(const std::uint32_t dpd_bits, std::uint8_t& d3, std::uint8_t& d2, std::uint8_t& d1) -> void { // DPD decoding logic as per IEEE 754-2008 std::uint8_t b[10] {}; for (int i = 0; i < 10; ++i) { b[i] = static_cast((dpd_bits >> (9 - i)) & 0b1); } // See table 3.3 for the flow of decoding // Values are b6, b7, b8, b3, b4 // 0XXXX if (b[6] == 0U) { d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); d3 = static_cast((b[7] << 2U) + (b[8] << 1U) + b[9]); } // 100XX else if (b[6] == 1U && b[7] == 0U && b[8] == 0U) { d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); d3 = static_cast(8U + b[9]); } // 101XX else if (b[6] == 1U && b[7] == 0U && b[8] == 1U) { d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); d2 = static_cast(8U + b[5]); d3 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[9]); } // 110XX else if (b[6] == 1U && b[7] == 1U && b[8] == 0U) { d1 = static_cast(8U + b[2]); d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); d3 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[9]); } // 11100 else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 0U && b[4] == 0U) { d1 = static_cast(8U + b[2]); d2 = static_cast(8U + b[5]); d3 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[9]); } // 11101 else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 0U && b[4] == 1U) { d1 = static_cast(8U + b[2]); d2 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[5]); d3 = static_cast(8U + b[9]); } // 11110 else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 1U && b[4] == 0U) { d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); d2 = static_cast(8U + b[5]); d3 = static_cast(8U + b[9]); } // 11111 else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 1U && b[4] == 1U) { d1 = static_cast(8U + b[2]); d2 = static_cast(8U + b[5]); d3 = static_cast(8U + b[9]); } // LCOV_EXCL_START else { BOOST_DECIMAL_UNREACHABLE; } // LCOV_EXCL_STOP } } // namespace detail BOOST_DECIMAL_EXPORT template constexpr auto to_dpd_d32(const DecimalType val) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t) { static_assert(std::is_same::value || std::is_same::value, "The input must be a 32-bit decimal type"); // In the non-finite cases, the encodings are the same // 3.5.2.a and 3.5.2.b if (!isfinite(val)) { return to_bid(val); } const auto sign {val.isneg()}; const auto exp {val.unbiased_exponent()}; const auto significand {val.full_significand()}; std::uint32_t dpd {}; // Set the sign bit as applicable if (sign) { dpd |= detail::d32_sign_mask; } // Break the significand down into the 7 declets are needed std::uint8_t d[std::numeric_limits::digits10] {}; auto temp_sig {significand}; for (int i = 6; i >= 0; --i) { d[i] = static_cast(temp_sig % 10U); temp_sig /= 10U; } BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); BOOST_DECIMAL_ASSERT(temp_sig == 0); // We now need to capture what the leading two bits of the exponent are, // since they are stored in the combination field constexpr std::uint32_t leading_two_exp_bits_mask {0b11000000}; const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 6U}; BOOST_DECIMAL_ASSERT(leading_two_bits >= 0 && leading_two_bits <= 2); constexpr std::uint32_t trailing_exp_bits_mask {0b00111111}; const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; std::uint32_t combination_field_bits {}; // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field // See 3.5.2.c.1 // If d0 is 8 or 9 then we follow section i if (d[0] >= 8) { const auto d0_is_nine {d[0] == 9}; switch (leading_two_bits) { case 0U: combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; break; case 1U: combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; break; case 2U: combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // If d0 is 0 to 7 then we follow section II else { // In here the value of d[0] = 4*G2 + 2*G3 + G4 const auto d0_mask {static_cast(d[0])}; switch (leading_two_bits) { case 0U: // 00XXX combination_field_bits |= d0_mask; break; case 1U: // 01XXX combination_field_bits = 0b01000; combination_field_bits |= d0_mask; break; case 2U: // 10XXX combination_field_bits = 0b10000; combination_field_bits |= d0_mask; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // The only thing we have left to compute now is the bit patterns of d[1] - d[6] const auto declet_1 {static_cast(detail::encode_dpd(d[1], d[2], d[3]))}; const auto declet_2 {static_cast(detail::encode_dpd(d[4], d[5], d[6]))}; // Now we can do final assembly of the number dpd |= (combination_field_bits << 26U); dpd |= (trailing_exp_bits << 20U); dpd |= (declet_1 << 10U); dpd |= declet_2; return dpd; } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd_d32(const std::uint32_t dpd) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { static_assert(std::is_same::value || std::is_same::value, "Target decimal type must be 32-bits"); const auto sign {(dpd & detail::d32_sign_mask) != 0}; // First we check for non-finite values // Since they are in the same initial format as BID it's easy to check with our existing masks if ((dpd & detail::d32_inf_mask) == detail::d32_inf_mask) { if ((dpd & detail::d32_snan_mask) == detail::d32_snan_mask) { return sign ? -std::numeric_limits::signaling_NaN() : std::numeric_limits::signaling_NaN(); } else if ((dpd & detail::d32_nan_mask) == detail::d32_nan_mask) { return sign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); } else { return sign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); } } constexpr std::uint32_t dpd_d32_exponent_mask {UINT32_C(0x3F00000)}; constexpr std::uint32_t dpd_d32_significand_mask {UINT32_C(0xFFFFF)}; constexpr std::uint32_t dpd_d32_combination_mask {UINT32_C(0x7C000000)}; // The bit lengths are the same as used in the standard bid format const auto combination_field_bits {(dpd & dpd_d32_combination_mask) >> 26U}; const auto exponent_field_bits {(dpd & dpd_d32_exponent_mask) >> 20U}; const auto significand_bits {(dpd & dpd_d32_significand_mask)}; // Case 1: 3.5.2.c.1.i // Combination field bits are 110XX or 11110X std::uint32_t d0 {}; std::uint32_t leading_biased_exp_bits {}; if (combination_field_bits >= 0b11000) { // d0 = 8 + G4 // Must be equal to 8 or 9 d0 = 8U + (combination_field_bits & 0b00001); BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); // leading exp bits are 2*G2 + G3 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Case 2: 3.5.2.c.1.ii // Combination field bits are 0XXXX or 10XXX else { // d0 = 4 * G2 + 2 * G3 + G4 // Must be in the range 0-7 d0 = combination_field_bits & 0b00111; BOOST_DECIMAL_ASSERT(d0 <= 7); // Leading exp bits are 2 * G0 + G1 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Now that we have the bits we can calculate the exponents value const auto complete_exp {(leading_biased_exp_bits << 6U) + exponent_field_bits}; const auto exp {static_cast(complete_exp) - detail::bias_v}; // We can now decode the remainder of the significand to recover the value std::uint8_t digits[7] {}; digits[0] = static_cast(d0); constexpr std::uint32_t seven_digits_mask {UINT32_C(0x3FF)}; const auto significand_low {significand_bits & seven_digits_mask}; detail::decode_dpd(significand_low, digits[6], digits[5], digits[4]); const auto significand_high {(significand_bits & UINT32_C(0xFFC00)) >> 10U}; BOOST_DECIMAL_ASSERT(significand_high <= seven_digits_mask); detail::decode_dpd(significand_high, digits[3], digits[2], digits[1]); // Now we can assemble the significand std::uint32_t significand {}; for (std::uint32_t i {}; i < 7U; ++i) { significand += digits[i] * detail::pow10(6 - i); } return DecimalType{significand, exp, sign}; } BOOST_DECIMAL_EXPORT template constexpr auto to_dpd_d64(const DecimalType val) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint64_t) { static_assert(std::is_same::value || std::is_same::value, "The input must be a 64-bit decimal type"); // In the non-finite cases the encodings are the same // 3.5.2.a and 3.5.2.b if (!isfinite(val)) { return to_bid(val); } const auto sign {val.isneg()}; const auto exp {val.unbiased_exponent()}; const auto significand {val.full_significand()}; std::uint64_t dpd {}; // Set the sign bit as applicable if (sign) { dpd |= detail::d64_sign_mask; } std::uint8_t d[std::numeric_limits::digits10] {}; auto temp_sig {significand}; for (int i = 15; i >= 0; --i) { d[i] = static_cast(temp_sig % 10U); temp_sig /= 10U; } BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); BOOST_DECIMAL_ASSERT(temp_sig == 0); constexpr std::uint64_t leading_two_exp_bits_mask {0b1100000000}; const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 8U}; BOOST_DECIMAL_ASSERT(leading_two_bits >= 0 && leading_two_bits <= 2); constexpr std::uint64_t trailing_exp_bits_mask {0b0011111111}; const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; std::uint64_t combination_field_bits {}; // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field // See 3.5.2.c.1 // If d0 is 8 or 9 then we follow section i if (d[0] >= 8) { const auto d0_is_nine {d[0] == 9}; switch (leading_two_bits) { case 0U: combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; break; case 1U: combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; break; case 2U: combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // If d0 is 0 to 7 then we follow section II else { // In here the value of d[0] = 4*G2 + 2*G3 + G4 const auto d0_mask {static_cast(d[0])}; switch (leading_two_bits) { case 0U: // 00XXX combination_field_bits |= d0_mask; break; case 1U: // 01XXX combination_field_bits = 0b01000; combination_field_bits |= d0_mask; break; case 2U: // 10XXX combination_field_bits = 0b10000; combination_field_bits |= d0_mask; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // Write the now known combination field and trailing exp bits to the result dpd |= (combination_field_bits << 58U); dpd |= (trailing_exp_bits << 50U); // Now we need to encode all the declets // Once we have the declet right it into the result int offset {4}; for (std::size_t i {1}; i < 15; i += 3U) { const auto declet {static_cast(detail::encode_dpd(d[i], d[i + 1], d[i + 2]))}; dpd |= (declet << (10 * offset)); --offset; } return dpd; } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd_d64(const std::uint64_t dpd) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { static_assert(std::is_same::value || std::is_same::value, "Target decimal type must be 64-bits"); const auto sign {(dpd & detail::d64_sign_mask) != 0}; // First we check for non-finite values // Since they are in the same initial format as BID it's easy to check with our existing masks if ((dpd & detail::d64_inf_mask) == detail::d64_inf_mask) { if ((dpd & detail::d64_snan_mask) == detail::d64_snan_mask) { return sign ? -std::numeric_limits::signaling_NaN() : std::numeric_limits::signaling_NaN(); } else if ((dpd & detail::d64_nan_mask) == detail::d64_nan_mask) { return sign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); } else { return sign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); } } // The bit lengths are the same as used in the standard bid format constexpr std::uint64_t dpd_d64_combination_field_mask {UINT64_C(0x7C00000000000000)}; constexpr std::uint64_t dpd_d64_exponent_field_mask {UINT64_C(0x3FC000000000000)}; constexpr std::uint64_t dpd_d64_significand_field_mask {UINT64_C(0x3FFFFFFFFFFFF)}; const auto combination_field_bits {(dpd & dpd_d64_combination_field_mask) >> 58U}; const auto exponent_field_bits {(dpd & dpd_d64_exponent_field_mask) >> 50U}; auto significand_bits {(dpd & dpd_d64_significand_field_mask)}; // Case 1: 3.5.2.c.1.i // Combination field bits are 110XX or 11110X std::uint64_t d0 {}; std::uint64_t leading_biased_exp_bits {}; if (combination_field_bits >= 0b11000) { // d0 = 8 + G4 // Must be equal to 8 or 9 d0 = 8U + (combination_field_bits & 0b00001); BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); // leading exp bits are 2*G2 + G3 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Case 2: 3.5.2.c.1.ii // Combination field bits are 0XXXX or 10XXX else { // d0 = 4 * G2 + 2 * G3 + G4 // Must be in the range 0-7 d0 = combination_field_bits & 0b00111; BOOST_DECIMAL_ASSERT(d0 <= 7); // Leading exp bits are 2 * G0 + G1 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Now that we have the bits we can calculate the exponents value const auto complete_exp {(leading_biased_exp_bits << 8U) + exponent_field_bits}; const auto exp {static_cast(complete_exp) - detail::bias_v}; // We can now decode the remainder of the significand to recover the value std::uint8_t digits[16] {}; digits[0] = static_cast(d0); for (int i = 15; i > 0; i -= 3) { constexpr std::uint32_t declet_mask {UINT32_C(0x3FF)}; const auto declet_bits {static_cast(significand_bits & declet_mask)}; significand_bits >>= 10U; detail::decode_dpd(declet_bits, digits[i], digits[i - 1], digits[i - 2]); } std::uint64_t significand {}; for (std::uint64_t i {}; i < 16U; ++i) { significand += digits[i] * detail::pow10(15 - i); } return DecimalType{significand, exp, sign}; } BOOST_DECIMAL_EXPORT template constexpr auto to_dpd_d128(const DecimalType val) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, boost::int128::uint128_t) { static_assert(std::is_same::value || std::is_same::value, "The input must be a 128-bit decimal type"); // In the non-finite cases the encodings are the same // 3.5.2.a and 3.5.2.b if (!isfinite(val)) { return to_bid(val); } const auto sign {val.isneg()}; const auto exp {val.unbiased_exponent()}; const auto significand {val.full_significand()}; int128::uint128_t dpd {}; // Set the sign bit as applicable if (sign) { dpd.high |= detail::d128_sign_mask; } constexpr int num_digits {std::numeric_limits::digits10}; std::uint8_t d[static_cast(num_digits)] {}; auto temp_sig {significand}; for (int i = num_digits - 1; i >= 0; --i) { d[i] = static_cast(temp_sig % 10U); temp_sig /= 10U; } BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); BOOST_DECIMAL_ASSERT(temp_sig == 0U); constexpr std::uint64_t leading_two_exp_bits_mask {0b11000000000000}; const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 12U}; constexpr std::uint64_t trailing_exp_bits_mask {0b00111111111111}; const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; std::uint64_t combination_field_bits {}; // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field // See 3.5.2.c.1 // If d0 is 8 or 9 then we follow section i if (d[0] >= 8) { const auto d0_is_nine {d[0] == 9}; switch (leading_two_bits) { // The decimal128_t case never uses the combination field like the other types, // since the significand always fits inside the allotted number of bits. // I don't believe this path will ever be taken, but it's correct case 0U: combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; break; case 1U: combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; break; case 2U: combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // If d0 is 0 to 7 then we follow section II else { // In here the value of d[0] = 4*G2 + 2*G3 + G4 const auto d0_mask {static_cast(d[0])}; switch (leading_two_bits) { case 0U: // 00XXX combination_field_bits |= d0_mask; break; case 1U: // 01XXX combination_field_bits = 0b01000; combination_field_bits |= d0_mask; break; case 2U: // 10XXX combination_field_bits = 0b10000; combination_field_bits |= d0_mask; break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; // LCOV_EXCL_STOP } } // Write the now know combination field and trailing exp bits to the result dpd.high |= (combination_field_bits << 58U); dpd.high |= (trailing_exp_bits << 46U); // Now we have to encode all 11 of the declets int offset {10}; for (std::size_t i {1}; i < num_digits - 1; i += 3U) { const auto declet {static_cast(detail::encode_dpd(d[i], d[i + 1], d[i + 2]))}; dpd |= int128::uint128_t(declet << (10 * offset)); --offset; } return dpd; } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd_d128(const int128::uint128_t dpd) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { static_assert(std::is_same::value || std::is_same::value, "Target decimal type must be 128-bits"); const auto sign {(dpd.high & detail::d128_sign_mask) != 0}; if ((dpd & detail::d128_inf_mask) == detail::d128_inf_mask) { if ((dpd & detail::d128_snan_mask) == detail::d128_snan_mask) { return sign ? -std::numeric_limits::signaling_NaN() : std::numeric_limits::signaling_NaN(); } else if ((dpd & detail::d128_nan_mask) == detail::d128_nan_mask) { return sign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); } else { return sign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); } } constexpr std::uint64_t d128_dpd_combination_field_mask_high_bits {UINT64_C(0x7C00000000000000)}; constexpr std::uint64_t d128_dpd_exponent_mask_high_bits {UINT64_C(0x3FFC00000000000)}; constexpr int128::uint128_t d128_dpd_significand_mask {UINT64_C(0x3FFFFFFFFFFF), UINT64_MAX}; // The bit lengths are the same as used in the standard bid format const auto combination_field_bits {(dpd.high & d128_dpd_combination_field_mask_high_bits) >> 58U}; const auto exponent_field_bits {(dpd.high & d128_dpd_exponent_mask_high_bits) >> 46U}; auto significand_bits {(dpd & d128_dpd_significand_mask)}; // Case 1: 3.5.2.c.1.i // Combination field bits are 110XX or 11110X std::uint64_t d0 {}; std::uint64_t leading_biased_exp_bits {}; if (combination_field_bits >= 0b11000) { // d0 = 8 + G4 // Must be equal to 8 or 9 d0 = 8U + (combination_field_bits & 0b00001); BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); // leading exp bits are 2*G2 + G3 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Case 2: 3.5.2.c.1.ii // Combination field bits are 0XXXX or 10XXX else { // d0 = 4 * G2 + 2 * G3 + G4 // Must be in the range 0-7 d0 = combination_field_bits & 0b00111; BOOST_DECIMAL_ASSERT(d0 <= 7); // Leading exp bits are 2 * G0 + G1 // Must be equal to 0, 1 or 2 leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); } // Now that we have the bits we can calculate the exponents value const auto complete_exp {(leading_biased_exp_bits << 12U) + exponent_field_bits}; const auto exp {static_cast(complete_exp) - detail::bias_v}; // We can now decode the remainder of the significand to recover the value constexpr auto num_digits {std::numeric_limits::digits10}; std::uint8_t digits[static_cast(num_digits)] {}; digits[0] = static_cast(d0); for (int i = num_digits - 1; i > 0; i -= 3) { constexpr std::uint32_t declet_mask {UINT32_C(0x3FF)}; const auto declet_bits {static_cast(significand_bits & declet_mask)}; significand_bits >>= 10U; detail::decode_dpd(declet_bits, digits[i], digits[i - 1], digits[i - 2]); } int128::uint128_t significand {}; for (int i {}; i < num_digits; ++i) { significand += static_cast(digits[i]) * detail::pow10(static_cast((num_digits - 1) - i)); } return DecimalType{significand, exp, sign}; } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal32_t val) noexcept -> std::uint32_t { return to_dpd_d32(val); } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal_fast32_t val) noexcept -> std::uint32_t { return to_dpd_d32(val); } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal64_t val) noexcept -> std::uint64_t { return to_dpd_d64(val); } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal_fast64_t val) noexcept -> std::uint64_t { return to_dpd_d64(val); } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal128_t val) noexcept -> int128::uint128_t { return to_dpd_d128(val); } BOOST_DECIMAL_EXPORT constexpr auto to_dpd(const decimal_fast128_t& val) noexcept -> int128::uint128_t { return to_dpd_d128(val); } BOOST_DECIMAL_EXPORT template constexpr auto to_dpd(const DecimalType val) noexcept { static_assert(detail::is_decimal_floating_point_v, "Must be a decimal floating point type."); return to_dpd(val); } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd(const std::uint32_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { return from_dpd_d32(bits); } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd(const std::uint64_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { return from_dpd_d64(bits); } BOOST_DECIMAL_EXPORT template constexpr auto from_dpd(const int128::uint128_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { return from_dpd_d128(bits); } #ifdef BOOST_DECIMAL_HAS_INT128 BOOST_DECIMAL_EXPORT template constexpr auto from_dpd(const detail::builtin_uint128_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) { const int128::uint128_t converted_bits {bits}; return from_dpd_d128(converted_bits); } #endif // BOOST_DECIMAL_HAS_INT128 } // namespace decimal } // namespace boost #endif // BOOST_DECIMAL_DPD_CONVERSION_HPP