Skip to content

Commit 0b2cffe

Browse files
authored
Merge pull request #1393 from boostorg/decompose
Add public decompose operation
2 parents 4353200 + 9afcfbe commit 0b2cffe

12 files changed

Lines changed: 341 additions & 0 deletions

File tree

doc/modules/ROOT/pages/cmath.adoc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,32 @@ If both operands are infinity, the result is infinity, with the same sign as x.
654654

655655
The quantize functions do not signal underflow.
656656

657+
=== `decompose`
658+
[source, c++]
659+
----
660+
#include <boost/decimal/cmath.hpp>
661+
662+
namespace boost {
663+
namespace decimal {
664+
665+
template <typename Decimal>
666+
struct decimal_components
667+
{
668+
using significand_type = typename Decimal::significand_type;
669+
using biased_exponent_type = typename Decimal::biased_exponent_type;
670+
671+
significand_type sig {};
672+
biased_exponent_type exp {};
673+
bool sign {};
674+
};
675+
676+
template <typename Decimal>
677+
constexpr auto decompose(const Decimal value) noexcept -> decimal_components<Decimal>
678+
679+
} // namespace decimal
680+
} // namespace boost
681+
----
682+
657683
=== `frexp10`
658684

659685
[source, c++]
@@ -694,6 +720,51 @@ Similar to the `frexp10` function above, but rather than returning the normalize
694720
This removes the effects of xref:cohorts.adoc[cohorts] on the IEEE 754 compliant types which for the example of `decimal32_t` means the significand will be in the range [1'000'000, 9'999'999].
695721
This function has *NO* effect on fast types since they are always normalized internally.
696722

723+
.This https://github.com/boostorg/decimal/blob/develop/example/decompose_frexp10_normalize.cpp[example] demonstrates the difference between `decompose`, `frexp10` and `normalize` on both IEEE and fast types.
724+
====
725+
[source, c++]
726+
----
727+
include::example$decompose_frexp10_normalize.cpp[]
728+
----
729+
730+
Output:
731+
----
732+
IEEE decimal32_t (cohort is preserved by the encoding):
733+
734+
value = 300
735+
decompose : sig = 3, exp = 2, sign = 0
736+
frexp10 : sig = 3000000, exp = -4
737+
normalize = 300
738+
739+
value = 300
740+
decompose : sig = 300, exp = 0, sign = 0
741+
frexp10 : sig = 3000000, exp = -4
742+
normalize = 300
743+
744+
value = 300
745+
decompose : sig = 3000000, exp = -4, sign = 0
746+
frexp10 : sig = 3000000, exp = -4
747+
normalize = 300
748+
749+
decimal_fast32_t (always normalized internally):
750+
751+
value = 300
752+
decompose : sig = 3000000, exp = -4, sign = 0
753+
frexp10 : sig = 3000000, exp = -4
754+
normalize = 300
755+
756+
value = 300
757+
decompose : sig = 3000000, exp = -4, sign = 0
758+
frexp10 : sig = 3000000, exp = -4
759+
normalize = 300
760+
761+
value = 300
762+
decompose : sig = 3000000, exp = -4, sign = 0
763+
frexp10 : sig = 3000000, exp = -4
764+
normalize = 300
765+
----
766+
====
767+
697768
=== `rescale`
698769

699770
[source, c++]
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2026 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
//
5+
// This file demonstrates the difference between decompose, frexp10 and
6+
// normalize, all of which inspect the internal representation of a
7+
// decimal floating point value but expose it in different ways.
8+
9+
#include <boost/decimal/decimal32_t.hpp> // For the type decimal32_t
10+
#include <boost/decimal/decimal_fast32_t.hpp>// For the type decimal_fast32_t
11+
#include <boost/decimal/cmath.hpp> // For decompose, frexp10, normalize
12+
#include <boost/decimal/iostream.hpp> // For decimal support for <iostream>
13+
#include <array>
14+
#include <iostream>
15+
16+
template <typename Decimal>
17+
void show(const Decimal value)
18+
{
19+
// decompose returns the encoded significand, biased exponent and sign.
20+
// For IEEE types this preserves the cohort that was used at construction.
21+
// For fast types the value is normalized in the constructor, so the
22+
// components always reflect the normalized form.
23+
const auto components {boost::decimal::decompose(value)};
24+
25+
// frexp10 returns the cohort-agnostic significand together with the
26+
// matching exponent written through the pointer argument. The significand
27+
// is normalized to the full precision of the type (for decimal32_t the
28+
// range is [1'000'000, 9'999'999]).
29+
int frexp_exp {};
30+
const auto frexp_sig {boost::decimal::frexp10(value, &frexp_exp)};
31+
32+
// normalize returns a decimal of the same type whose representation has
33+
// had the cohort effects removed. For fast types this is a no-op since
34+
// they are already normalized.
35+
const auto normalized {boost::decimal::normalize(value)};
36+
37+
std::cout << " value = " << value << '\n'
38+
<< " decompose : sig = " << components.sig
39+
<< ", exp = " << components.exp
40+
<< ", sign = " << components.sign << '\n'
41+
<< " frexp10 : sig = " << frexp_sig
42+
<< ", exp = " << frexp_exp << '\n'
43+
<< " normalize = " << normalized << "\n\n";
44+
}
45+
46+
int main()
47+
{
48+
// All three values compare equal but use different cohorts.
49+
constexpr std::array<boost::decimal::decimal32_t, 3> ieee_values {
50+
boost::decimal::decimal32_t{3, 2},
51+
boost::decimal::decimal32_t{300, 0},
52+
boost::decimal::decimal32_t{3000000, -4}
53+
};
54+
55+
std::cout << "IEEE decimal32_t (cohort is preserved by the encoding):\n\n";
56+
for (const auto& v : ieee_values)
57+
{
58+
show(v);
59+
}
60+
61+
// The same set of inputs given to a fast type all encode identically.
62+
constexpr std::array<boost::decimal::decimal_fast32_t, 3> fast_values {
63+
boost::decimal::decimal_fast32_t{3, 2},
64+
boost::decimal::decimal_fast32_t{300, 0},
65+
boost::decimal::decimal_fast32_t{3000000, -4}
66+
};
67+
68+
std::cout << "decimal_fast32_t (always normalized internally):\n\n";
69+
for (const auto& v : fast_values)
70+
{
71+
show(v);
72+
}
73+
74+
return 0;
75+
}

include/boost/decimal/cmath.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include <boost/decimal/detail/cmath/beta.hpp>
8181
#include <boost/decimal/detail/cmath/normalize.hpp>
8282
#include <boost/decimal/detail/cmath/comparetotal.hpp>
83+
#include <boost/decimal/detail/cmath/decompose.hpp>
8384
#include <boost/decimal/numbers.hpp>
8485

8586
// Macros from 3.6.2

include/boost/decimal/decimal128_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,9 @@ BOOST_DECIMAL_EXPORT class decimal128_t final
564564
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
565565
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
566566

567+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
568+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
569+
567570
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto copysignd128(decimal128_t mag, decimal128_t sgn) noexcept -> decimal128_t;
568571
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto scalblnd128(decimal128_t num, long exp) noexcept -> decimal128_t;
569572
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto scalbnd128(decimal128_t num, int exp) noexcept -> decimal128_t;

include/boost/decimal/decimal32_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ BOOST_DECIMAL_EXPORT class decimal32_t final // NOLINT(cppcoreguidelines-special
578578
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
579579
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
580580

581+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
582+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
583+
581584
friend constexpr auto scalbnd32(decimal32_t num, int exp) noexcept -> decimal32_t;
582585
friend constexpr auto scalblnd32(decimal32_t num, long exp) noexcept -> decimal32_t;
583586

include/boost/decimal/decimal64_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ BOOST_DECIMAL_EXPORT class decimal64_t final
583583
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
584584
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
585585

586+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
587+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
588+
586589
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto copysignd64(decimal64_t mag, decimal64_t sgn) noexcept -> decimal64_t;
587590
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto scalbnd64(decimal64_t num, int exp) noexcept -> decimal64_t;
588591
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto scalblnd64(decimal64_t num, long exp) noexcept -> decimal64_t;

include/boost/decimal/decimal_fast128_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,9 @@ BOOST_DECIMAL_EXPORT class alignas(16) decimal_fast128_t final
492492
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
493493
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
494494

495+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
496+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
497+
495498
friend constexpr auto copysignd128f(decimal_fast128_t mag, decimal_fast128_t sgn) noexcept -> decimal_fast128_t;
496499
friend constexpr auto scalblnd128f(decimal_fast128_t num, long exp) noexcept -> decimal_fast128_t;
497500
friend constexpr auto scalbnd128f(decimal_fast128_t num, int exp) noexcept -> decimal_fast128_t;

include/boost/decimal/decimal_fast32_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,9 @@ BOOST_DECIMAL_EXPORT class alignas(4) decimal_fast32_t final
480480
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
481481
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
482482

483+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
484+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
485+
483486
friend constexpr auto copysignd32f(decimal_fast32_t mag, decimal_fast32_t sgn) noexcept -> decimal_fast32_t;
484487
friend constexpr auto scalbnd32f(decimal_fast32_t num, int exp) noexcept -> decimal_fast32_t;
485488
friend constexpr auto scalblnd32f(decimal_fast32_t num, long exp) noexcept -> decimal_fast32_t;

include/boost/decimal/decimal_fast64_t.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ BOOST_DECIMAL_EXPORT class alignas(8) decimal_fast64_t final
503503
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
504504
friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type;
505505

506+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
507+
friend BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T x) noexcept;
508+
506509
friend constexpr auto copysignd64f(decimal_fast64_t mag, decimal_fast64_t sgn) noexcept -> decimal_fast64_t;
507510
friend constexpr auto scalbnd64f(decimal_fast64_t num, int exp) noexcept -> decimal_fast64_t;
508511
friend constexpr auto scalblnd64f(decimal_fast64_t num, long exp) noexcept -> decimal_fast64_t;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2026 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_DETAIL_CMATH_DECOMPOSE_HPP
6+
#define BOOST_DECIMAL_DETAIL_CMATH_DECOMPOSE_HPP
7+
8+
#include <boost/decimal/detail/config.hpp>
9+
#include <boost/decimal/detail/concepts.hpp>
10+
#include <boost/decimal/detail/components.hpp>
11+
12+
namespace boost { namespace decimal {
13+
14+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
15+
BOOST_DECIMAL_CUDA_CONSTEXPR auto decompose(const T value) noexcept
16+
{
17+
return value.to_components();
18+
}
19+
20+
} // namespace decimal
21+
} // namespace boost
22+
23+
#endif // BOOST_DECIMAL_DETAIL_CMATH_DECOMPOSE_HPP

0 commit comments

Comments
 (0)