Skip to content

Commit 48e9a87

Browse files
authored
Merge pull request #743 from cppalliance/dpd128
2 parents 16712c0 + d55f599 commit 48e9a87

File tree

6 files changed

+267
-3
lines changed

6 files changed

+267
-3
lines changed

doc/decimal/conversions.adoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,31 @@ constexpr std::uint32_t to_dpd_d32(decimal32 val) noexcept;
8484
8585
constexpr std::uint32_t to_dpd_d32f(decimal32_fast val) noexcept;
8686
87+
constexpr std::uint64_t to_dpd_d64(decimal64 val) noexcept;
88+
89+
constexpr std::uint64_t to_dpd_d64f(decimal64_fast val) noexcept;
90+
91+
constexpr detail::uint128 to_dpd_d128(decimal128 val) noexcept;
92+
93+
constexpr detail::uint128 to_dpd_d128f(decimal128_fast val) noexcept;
94+
8795
template <typename T>
8896
constexpr auto to_dpd(T val) noexcept;
8997
9098
template <typename T = decimal32_fast>
9199
constexpr T from_dpd(std::uint32_t bits) noexcept;
92100
101+
template <typename T = decimal64_fast>
102+
constexpr T from_dpd(std::uint64_t bits) noexcept;
103+
104+
template <typename T = decimal128_fast>
105+
constexpr T from_dpd(detail::uint128 bits) noexcept;
106+
107+
#ifdef BOOST_DECIMAL_HAS_INT128
108+
template <typename T = decimal128_fast>
109+
constexpr T from_dpd(unsigned __int128 bits) noexcept;
110+
#endif
111+
93112
} // namespace decimal
94113
} // namespace boost
95114
----

include/boost/decimal/bid_conversion.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ constexpr auto from_bid_d128(detail::uint128 bits) noexcept -> decimal128
7777
}
7878

7979
#ifdef BOOST_DECIMAL_HAS_INT128
80-
constexpr auto from_bits_d128(detail::uint128_t bits) noexcept -> decimal128
80+
constexpr auto from_bid_d128(detail::uint128_t bits) noexcept -> decimal128
8181
{
8282
return from_bits(bits);
8383
}

include/boost/decimal/decimal128.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ BOOST_DECIMAL_EXPORT class decimal128 final
236236
friend constexpr auto from_bid_d128(detail::uint128_t bits) noexcept -> decimal128;
237237
#endif
238238

239+
template <typename DecimalType>
240+
friend constexpr auto to_dpd_d128(DecimalType val) noexcept
241+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128);
242+
239243
public:
240244
// 3.2.4.1 construct/copy/destroy
241245
constexpr decimal128() noexcept = default;

include/boost/decimal/decimal128_fast.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class decimal128_fast final
123123

124124
friend constexpr auto not_finite(const decimal128_fast& val) noexcept -> bool;
125125

126+
template <typename DecimalType>
127+
friend constexpr auto to_dpd_d128(DecimalType val) noexcept
128+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128);
129+
126130
public:
127131
constexpr decimal128_fast() noexcept = default;
128132

include/boost/decimal/dpd_conversion.hpp

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <boost/decimal/bid_conversion.hpp>
99
#include <boost/decimal/detail/config.hpp>
1010
#include <boost/decimal/detail/concepts.hpp>
11+
#include <boost/decimal/detail/emulated128.hpp>
1112

1213
#ifndef BOOST_DECIMAL_BUILD_MODULE
1314
#include <cstdint>
@@ -425,6 +426,9 @@ template <typename DecimalType = decimal32_fast>
425426
constexpr auto from_dpd_d32(std::uint32_t dpd) noexcept
426427
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
427428
{
429+
static_assert(std::is_same<DecimalType, decimal32>::value || std::is_same<DecimalType, decimal32_fast>::value,
430+
"Target decimal type must be 32-bits");
431+
428432
// First we check for non-finite values
429433
// Since they are in the same initial format as BID it's easy to check with our existing masks
430434
if ((dpd & detail::d32_inf_mask) == detail::d32_inf_mask)
@@ -618,6 +622,9 @@ template <typename DecimalType = decimal64_fast>
618622
constexpr auto from_dpd_d64(std::uint64_t dpd) noexcept
619623
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
620624
{
625+
static_assert(std::is_same<DecimalType, decimal64>::value || std::is_same<DecimalType, decimal64_fast>::value,
626+
"Target decimal type must be 64-bits");
627+
621628
// First we check for non-finite values
622629
// Since they are in the same initial format as BID it's easy to check with our existing masks
623630
if ((dpd & detail::d64_inf_mask) == detail::d64_inf_mask)
@@ -696,6 +703,201 @@ constexpr auto from_dpd_d64(std::uint64_t dpd) noexcept
696703
return DecimalType{significand, exp, sign};
697704
}
698705

706+
template <typename DecimalType>
707+
constexpr auto to_dpd_d128(DecimalType val) noexcept
708+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128)
709+
{
710+
static_assert(std::is_same<DecimalType, decimal128>::value ||
711+
std::is_same<DecimalType, decimal128_fast>::value, "The input must be a 128-bit decimal type");
712+
713+
// In the non-finite cases the encodings are the same
714+
// 3.5.2.a and 3.5.2.b
715+
if (!isfinite(val))
716+
{
717+
return to_bid(val);
718+
}
719+
720+
const auto sign {val.isneg()};
721+
const auto exp {val.unbiased_exponent()};
722+
const auto significand {val.full_significand()};
723+
724+
detail::uint128 dpd {};
725+
726+
// Set the sign bit as applicable
727+
if (sign)
728+
{
729+
dpd.high |= detail::d128_sign_mask.high;
730+
}
731+
732+
constexpr int num_digits {std::numeric_limits<DecimalType>::digits10};
733+
std::uint8_t d[static_cast<std::size_t>(num_digits)] {};
734+
auto temp_sig {significand};
735+
for (int i = num_digits - 1; i >= 0; --i)
736+
{
737+
d[i] = static_cast<std::uint8_t>(temp_sig % 10U);
738+
temp_sig /= 10U;
739+
}
740+
BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9);
741+
BOOST_DECIMAL_ASSERT(temp_sig == 0);
742+
743+
constexpr std::uint64_t leading_two_exp_bits_mask {0b11000000000000};
744+
const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 12U};
745+
constexpr std::uint64_t trailing_exp_bits_mask {0b00111111111111};
746+
const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)};
747+
748+
std::uint64_t combination_field_bits {};
749+
750+
// 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
751+
// See 3.5.2.c.1
752+
// If d0 is 8 or 9 then we follow section i
753+
if (d[0] >= 8)
754+
{
755+
const auto d0_is_nine {d[0] == 9};
756+
switch (leading_two_bits)
757+
{
758+
case 0U:
759+
combination_field_bits = d0_is_nine ? 0b11001 : 0b11000;
760+
break;
761+
case 1U:
762+
combination_field_bits = d0_is_nine ? 0b11011 : 0b11010;
763+
break;
764+
case 2U:
765+
combination_field_bits = d0_is_nine ? 0b11101 : 0b11100;
766+
break;
767+
// LCOV_EXCL_START
768+
default:
769+
BOOST_DECIMAL_UNREACHABLE;
770+
// LCOV_EXCL_STOP
771+
}
772+
}
773+
// If d0 is 0 to 7 then we follow section II
774+
else
775+
{
776+
// In here the value of d[0] = 4*G2 + 2*G3 + G4
777+
const auto d0_mask {static_cast<std::uint64_t>(d[0])};
778+
switch (leading_two_bits)
779+
{
780+
case 0U:
781+
// 00XXX
782+
combination_field_bits |= d0_mask;
783+
break;
784+
case 1U:
785+
// 01XXX
786+
combination_field_bits = 0b01000;
787+
combination_field_bits |= d0_mask;
788+
break;
789+
case 2U:
790+
// 10XXX
791+
combination_field_bits = 0b10000;
792+
combination_field_bits |= d0_mask;
793+
break;
794+
// LCOV_EXCL_START
795+
default:
796+
BOOST_DECIMAL_UNREACHABLE;
797+
// LCOV_EXCL_STOP
798+
}
799+
}
800+
801+
// Write the now know combination field and trailing exp bits to the result
802+
dpd.high |= (combination_field_bits << 58U);
803+
dpd.high |= (trailing_exp_bits << 46U);
804+
805+
// Now we have to encode all 11 of the declets
806+
int offset {10};
807+
for (std::size_t i {1}; i < num_digits - 1; i += 3U)
808+
{
809+
const auto declet {static_cast<detail::uint128>(detail::encode_dpd(d[i], d[i + 1], d[i + 2]))};
810+
dpd |= detail::uint128(declet << (10 * offset));
811+
--offset;
812+
}
813+
814+
return dpd;
815+
}
816+
817+
template <typename DecimalType = decimal128_fast>
818+
constexpr auto from_dpd_d128(detail::uint128 dpd) noexcept
819+
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
820+
{
821+
static_assert(std::is_same<DecimalType, decimal128>::value || std::is_same<DecimalType, decimal128_fast>::value,
822+
"Target decimal type must be 128-bits");
823+
824+
if ((dpd & detail::d128_inf_mask) == detail::d128_inf_mask)
825+
{
826+
if ((dpd & detail::d128_snan_mask) == detail::d128_snan_mask)
827+
{
828+
return std::numeric_limits<DecimalType>::signaling_NaN();
829+
}
830+
else if ((dpd & detail::d128_nan_mask) == detail::d128_nan_mask)
831+
{
832+
return std::numeric_limits<DecimalType>::quiet_NaN();
833+
}
834+
else
835+
{
836+
return std::numeric_limits<DecimalType>::infinity();
837+
}
838+
}
839+
840+
// The bit lengths are the same as used in the standard bid format
841+
const auto sign {(dpd.high & detail::d128_sign_mask.high) != 0};
842+
const auto combination_field_bits {(dpd.high & detail::d128_combination_field_mask.high) >> 58U};
843+
const auto exponent_field_bits {(dpd.high & detail::d128_exponent_mask.high) >> 46U};
844+
auto significand_bits {(dpd & detail::d128_significand_mask)};
845+
846+
// Case 1: 3.5.2.c.1.i
847+
// Combination field bits are 110XX or 11110X
848+
std::uint64_t d0 {};
849+
std::uint64_t leading_biased_exp_bits {};
850+
if (combination_field_bits >= 0b11000)
851+
{
852+
// d0 = 8 + G4
853+
// Must be equal to 8 or 9
854+
d0 = 8U + (combination_field_bits & 0b00001);
855+
BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9);
856+
857+
// leading exp bits are 2*G2 + G3
858+
// Must be equal to 0, 1 or 2
859+
leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U);
860+
BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U);
861+
}
862+
// Case 2: 3.5.2.c.1.ii
863+
// Combination field bits are 0XXXX or 10XXX
864+
else
865+
{
866+
// d0 = 4 * G2 + 2 * G3 + G4
867+
// Must be in the range 0-7
868+
d0 = combination_field_bits & 0b00111;
869+
BOOST_DECIMAL_ASSERT(d0 <= 7);
870+
871+
// Leading exp bits are 2 * G0 + G1
872+
// Must be equal to 0, 1 or 2
873+
leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U;
874+
BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U);
875+
}
876+
877+
// Now that we have the bits we can calculate the exponents value
878+
const auto complete_exp {(leading_biased_exp_bits << 12U) + exponent_field_bits};
879+
const auto exp {static_cast<std::int32_t>(complete_exp) - detail::bias_v<DecimalType>};
880+
881+
// We can now decode the remainder of the significand to recover the value
882+
constexpr auto num_digits {std::numeric_limits<DecimalType>::digits10};
883+
std::uint8_t digits[static_cast<std::size_t>(num_digits)] {};
884+
digits[0] = static_cast<std::uint8_t>(d0);
885+
for (int i = num_digits - 1; i > 0; i -= 3)
886+
{
887+
const auto declet_bits {static_cast<std::uint32_t>(significand_bits & 0b1111111111)};
888+
significand_bits >>= 10U;
889+
detail::decode_dpd(declet_bits, digits[i], digits[i - 1], digits[i - 2]);
890+
}
891+
892+
detail::uint128 significand {};
893+
for (int i {}; i < num_digits; ++i)
894+
{
895+
significand += static_cast<detail::uint128>(digits[i]) * detail::pow10(static_cast<detail::uint128>((num_digits - 1) - i));
896+
}
897+
898+
return DecimalType{significand, exp, sign};
899+
}
900+
699901
constexpr auto to_dpd(decimal32 val) noexcept -> std::uint32_t
700902
{
701903
return to_dpd_d32(val);
@@ -706,16 +908,26 @@ constexpr auto to_dpd(decimal32_fast val) noexcept -> std::uint32_t
706908
return to_dpd_d32(val);
707909
}
708910

709-
constexpr auto to_dpd(decimal64 val) -> std::uint64_t
911+
constexpr auto to_dpd(decimal64 val) noexcept -> std::uint64_t
710912
{
711913
return to_dpd_d64(val);
712914
}
713915

714-
constexpr auto to_dpd(decimal64_fast val) -> std::uint64_t
916+
constexpr auto to_dpd(decimal64_fast val) noexcept -> std::uint64_t
715917
{
716918
return to_dpd_d64(val);
717919
}
718920

921+
constexpr auto to_dpd(decimal128 val) noexcept -> detail::uint128
922+
{
923+
return to_dpd_d128(val);
924+
}
925+
926+
constexpr auto to_dpd(decimal128_fast val) noexcept -> detail::uint128
927+
{
928+
return to_dpd_d128(val);
929+
}
930+
719931
template <typename DecimalType>
720932
constexpr auto to_dpd(DecimalType val) noexcept
721933
{
@@ -737,6 +949,25 @@ constexpr auto from_dpd(std::uint64_t bits) noexcept
737949
return from_dpd_d64<DecimalType>(bits);
738950
}
739951

952+
template <typename DecimalType = decimal128_fast>
953+
constexpr auto from_dpd(detail::uint128 bits) noexcept
954+
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
955+
{
956+
return from_dpd_d128<DecimalType>(bits);
957+
}
958+
959+
#ifdef BOOST_DECIMAL_HAS_INT128
960+
961+
template <typename DecimalType = decimal128_fast>
962+
constexpr auto from_dpd(detail::uint128_t bits) noexcept
963+
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
964+
{
965+
const detail::uint128 converted_bits {bits};
966+
return from_dpd_d128<DecimalType>(converted_bits);
967+
}
968+
969+
#endif // BOOST_DECIMAL_HAS_INT128
970+
740971
} // namespace decimal
741972
} // namespace boost
742973

test/test_dpd_conversions.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,11 @@ int main()
7070
test_float_range<decimal64>();
7171
test_float_range<decimal64_fast>();
7272

73+
test<decimal128>();
74+
test<decimal128_fast>();
75+
76+
test_float_range<decimal128>();
77+
test_float_range<decimal128_fast>();
78+
7379
return boost::report_errors();
7480
}

0 commit comments

Comments
 (0)