Skip to content

Commit 00d6406

Browse files
authored
Merge pull request #1228 from cppalliance/examples
Improve `<charconv>` related docs and examples
2 parents d6c9c3e + 7d0b3c7 commit 00d6406

File tree

18 files changed

+909
-303
lines changed

18 files changed

+909
-303
lines changed

doc/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
** xref:decimal_fast32_t.adoc[]
2828
** xref:decimal_fast64_t.adoc[]
2929
** xref:decimal_fast128_t.adoc[]
30+
* xref:cohorts.adoc[]
3031
* xref:conversions.adoc[]
3132
* xref:literals.adoc[]
3233
* xref:numbers.adoc[]

doc/modules/ROOT/pages/charconv.adoc

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,4 @@ This class allows you to size buffers for `to_chars` without have to arbitrarily
193193
The `Precision` is defaulted to the maximum precision of the type, which is the same assumption that is made when you use `to_chars` with an unspecified precision.
194194
The `max_chars` variable is the largest of the 5 specified `chars_format` options.
195195

196-
The members can then be used to size buffers such as:
197-
198-
[source, c++]
199-
----
200-
#include <boost/decimal.hpp>
201-
#include <iostream>
202-
203-
int main()
204-
{
205-
using namespace boost::decimal;
206-
207-
decimal32_t val {5, -1};
208-
209-
char buffer[formatting_limits<decimal32_t>::max_chars];
210-
211-
auto r_to = to_chars(buffer, buffer + sizeof(buffer), val);
212-
*r_to.ptr = '\0';
213-
214-
std::cout << buffer << std::endl;
215-
216-
return 0;
217-
}
218-
219-
----
196+
The members can then be used to size buffers such as in our `<charconv>` example from the main examples page xref:examples.adoc#examples_charconv[here].

doc/modules/ROOT/pages/cmath.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ https://www.boost.org/LICENSE_1_0.txt
99
:idprefix: cmath_
1010

1111
Decimal contains overloads for all functions from `<cmath>`, and they have the same handling as built-in floating point types.
12-
They are also all constexpr with C\\++14 unlike the built-in floating point types which require either C++23 or 26.
12+
They are also all constexpr with pass:[C++14] unlike the built-in floating point types which require either pass:[C++23] or 26.
1313
Additionally, all functions are marked `noexcept`.
1414

1515
All of these functions are impacted by the global rounding mode as described in xref:cfenv.adoc[rounding modes] as well as the `DEC_FLT_EVAL_METHOD` from xref:cfloat.adoc[evaluation methods].
@@ -679,7 +679,7 @@ constexpr Decimal normalize(Decimal val) noexcept;
679679
----
680680

681681
Similar to the `frexp10` function above, but rather than returning the normalized significand, and exponent of the Decimal number, it returns a normalized number.
682-
This removes the effects of xref:basics.adoc[cohorts] on the IEEE 754 compliant types.
682+
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].
683683
This function has *NO* effect on fast types since they are always normalized internally.
684684

685685
=== `rescale`
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
////
2+
Copyright 2025 Matt Borland
3+
Distributed under the Boost Software License, Version 1.0.
4+
https://www.boost.org/LICENSE_1_0.txt
5+
////
6+
7+
[#cohorts]
8+
= Cohorts
9+
: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

Comments
 (0)