Skip to content

Commit 49fa5d0

Browse files
committed
Add cohort page to docs
1 parent 4d0fbb9 commit 49fa5d0

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

doc/modules/ROOT/pages/cohorts.adoc

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
----

doc/modules/ROOT/pages/overview.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Decimal floating point numbers avoid this issue by storing the significand in ba
2323
The other major difference between binary and decimal floating point types is that the latter allows for multiple representations of the same number.
2424
For example 1e5 could also be stored as 0.1e6, 0.01e7, so on and so forth.
2525
These are referred to as cohorts which binary does not have as there is only one way to represent each number in binary floating point.
26+
For more information on cohorts please see the xref:cohorts.adoc[cohort section] of the documentation.
2627

2728
== Use Cases
2829

examples/charconv_cohort_preservation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright 2025 Matt Borland
22
// Distributed under the Boost Software License, Version 1.0.
33
// https://www.boost.org/LICENSE_1_0.txt
4+
//
5+
// This file demonstrates the effects of cohorts and how to maintain them with <charconv>
46

57
#include <boost/decimal/decimal32_t.hpp> // For the type decimal32_t
68
#include <boost/decimal/charconv.hpp> // For decimal support for <charconv>

0 commit comments

Comments
 (0)