@@ -7,3 +7,137 @@ https://www.boost.org/LICENSE_1_0.txt
77[#cohorts]
88= Cohorts
99:idprefix: cohorts_
10+
11+ As stated in the library overview, one of the major differences between binary floating point and decimal floating point is the existence of cohorts.
12+ From IEEE 754-2008: "The set of representations a floating-point number maps to is called the floating-point
13+ number’s cohort; the members of a cohort are distinct representations of the same floating-point number."
14+
15+ For example `decimal32_t` has a precision of 7.
16+ That means all the following are valid ways to represent the number 300 using that type:
17+
18+ . 3e+2
19+ . 30e+1
20+ . 300e+0
21+ . 3000e-1
22+ . 30000e-2
23+ . 300000e-3
24+ . 3000000e-4
25+
26+ These values can be useful in certain applications, like preserving the precision of results.
27+ By default, most methods *DO NOT* observe cohorts.
28+ Below is an example of how cohorts can be preserved if one so wishes.
29+
30+ == Cohort Preserving `<charconv>` Example
31+
32+ [source, c++]
33+ ----
34+ // This file demonstrates the effects of cohorts and how to maintain them with <charconv>
35+
36+ #include <boost/decimal/decimal32_t.hpp> // For the type decimal32_t
37+ #include <boost/decimal/charconv.hpp> // For decimal support for <charconv>
38+ #include <boost/decimal/iostream.hpp> // For decimal support for <iostream>
39+ #include <iostream>
40+ #include <array>
41+ #include <cstring>
42+ #include <string>
43+
44+ static constexpr std::size_t N {7};
45+
46+ // All the following decimal values will compare equal,
47+ // but since they have different numbers of 0s in the significand they will not be bitwise equal
48+ constexpr std::array<boost::decimal::decimal32_t, N> decimals = {
49+ boost::decimal::decimal32_t{3, 2},
50+ boost::decimal::decimal32_t{30, 1},
51+ boost::decimal::decimal32_t{300, 0},
52+ boost::decimal::decimal32_t{3000, -1},
53+ boost::decimal::decimal32_t{30000, -2},
54+ boost::decimal::decimal32_t{300000, -3},
55+ boost::decimal::decimal32_t{3000000, -4},
56+ };
57+
58+ // These strings represent the same values as the constructed ones shown above
59+ constexpr std::array<const char*, N> strings = {
60+ "3e+02",
61+ "3.0e+02",
62+ "3.00e+02",
63+ "3.000e+02",
64+ "3.0000e+02",
65+ "3.00000e+02",
66+ "3.000000e+02",
67+ };
68+
69+ int main()
70+ {
71+ using boost::decimal::decimal32_t;
72+ using boost::decimal::from_chars;
73+ using boost::decimal::chars_format;
74+
75+ // In some instances we want to preserve the cohort of our values
76+ // In the above strings array all of these values compare equal,
77+ // but will NOT be bitwise equal once constructed.
78+
79+ for (std::size_t i = 0; i < N; ++i)
80+ {
81+ decimal32_t string_val;
82+ const auto r_from = from_chars(strings[i], string_val, chars_format::cohort_preserving_scientific);
83+
84+ if (!r_from)
85+ {
86+ // Unexpected failure
87+ return 1;
88+ }
89+
90+ for (std::size_t j = 0; j < N; ++j)
91+ {
92+ // Now that we have constructed a value from string
93+ // we can compare it bitwise to all the members of the decimal array
94+ // to show the difference between operator== and bitwise equality
95+ //
96+ // All members of a cohort are supposed to compare equal with operator==,
97+ // and likewise will hash equal to
98+ std::uint32_t string_val_bits;
99+ std::uint32_t constructed_val_bits;
100+
101+ std::memcpy(&string_val_bits, &string_val, sizeof(string_val_bits));
102+ std::memcpy(&constructed_val_bits, &decimals[j], sizeof(constructed_val_bits));
103+
104+ if (string_val == decimals[j])
105+ {
106+ std::cout << "Values are equal and ";
107+ if (string_val_bits == constructed_val_bits)
108+ {
109+ std::cout << "bitwise equal.\n";
110+ }
111+ else
112+ {
113+ std::cout << "NOT bitwise equal.\n";
114+ }
115+ }
116+ }
117+
118+ // The same chars_format option applies to to_chars which allows us to roundtrip the values
119+ char buffer[64] {};
120+ const auto r_to = to_chars(buffer, buffer + sizeof(buffer), string_val, chars_format::cohort_preserving_scientific);
121+
122+ if (!r_to)
123+ {
124+ // Unexpected failure
125+ return 1;
126+ }
127+
128+ *r_to.ptr = '\0'; // charconv does not null terminate per the C++ specification
129+
130+ if (std::strcmp(strings[i], buffer) == 0)
131+ {
132+ std::cout << "Successful Roundtrip\n\n";
133+ }
134+ else
135+ {
136+ std::cout << "Failed\n\n";
137+ return 1;
138+ }
139+ }
140+
141+ return 0;
142+ }
143+ ----
0 commit comments