Skip to content

Commit 50634b1

Browse files
authored
Merge pull request #1246 from cppalliance/1196
2 parents a6cbd3b + a4809e0 commit 50634b1

File tree

6 files changed

+188
-95
lines changed

6 files changed

+188
-95
lines changed

doc/modules/ROOT/pages/basics.adoc

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,27 +119,19 @@ For more information see: xref:cfenv.adoc[]
119119
The entire library can be accessed using the convenience header `<boost/decimal.hpp>`.
120120
A short example of the basic usage:
121121

122+
.This https://github.com/cppalliance/decimal/blob/develop/examples/first_example.cpp[example] shows the very basics on how to make a complete and working program using the decimal library
123+
====
122124
[source, c++]
123125
----
124-
#include <boost/decimal.hpp>
125-
#include <iostream>
126-
#include <iomanip>
127-
128-
int main()
129-
{
130-
using namespace boost::decimal;
131-
132-
// Outputs 0.30000000000000004
133-
std::cout << std::setprecision(17) << 0.1 + 0.2 << std::endl;
134-
135-
// Construct the two decimal values
136-
constexpr decimal64_t a {1, -1}; // 1e-1 or 0.1
137-
constexpr decimal64_t b {2, -1}; // 2e-1 or 0.2
126+
include::example$first_example.cpp[]
127+
----
138128
139-
// Outputs 0.30000000000000000
140-
std::cout << a + b << std::endl;
129+
.Expected Output
130+
....
131+
Using doubles:
132+
0.1 + 0.2 = 0.30000000000000004
141133
142-
return 0;
143-
}
144-
145-
----
134+
Using decimal64_t:
135+
0.1_DD + 0.2_DD = 0.3000000000000000
136+
....
137+
====

doc/modules/ROOT/pages/cfenv.adoc

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,55 +57,40 @@ rounding_mode fesetround(rounding_mode round) noexcept;
5757

5858
IMPORTANT: Much like `std::fesetround`, `boost::decimal::fesetround` is not thread safe.
5959

60-
Below is an example of the effects of changing the runtime rounding mode if your compiler supports it:
60+
[#examples_rounding_mode]
6161

62+
.This https://github.com/cppalliance/decimal/blob/develop/examples/rounding_mode.cpp[example] demonstrates how to set, get, and effects of the global rounding mode.
63+
====
6264
[source, c++]
6365
----
64-
#include <boost/decimal.hpp>
65-
#include <cassert>
66-
67-
int main()
68-
{
69-
using namespace boost::decimal::literals;
70-
71-
auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest
72-
73-
auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_upward);
74-
75-
assert(default_rounding_mode != new_rounding_mode);
76-
77-
const auto lhs {"5e+50"_DF};
78-
const auto rhs {"4e+40"_DF};
79-
80-
// With upward rounding the result will be "5.000001e+50"_DF
81-
// Even though the difference in order of magnitude is greater than the precision of the type,
82-
// any addition in this mode will result in at least a one ULP difference
83-
const auto upward_res {lhs + rhs};
84-
assert(upward_res == "5.000001e+50"_DF);
85-
86-
boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_downward);
87-
88-
// Similar to above in the downward rounding mode any subtraction will result in at least a one ULP difference
89-
const auto downward_res {lhs - rhs};
90-
assert(downward_res == "4.999999e+50"_DF);
91-
92-
return 0;
93-
}
66+
include::example$rounding_mode.cpp[]
9467
----
9568
69+
.Expected Output:
70+
....
71+
The default rounding mode is: fe_dec_to_nearest
72+
The current rounding mode is: fe_dec_upward
73+
lhs equals: 5e+50
74+
rhs equals: 4e+40
75+
Sum with upward rounding: 5.000001e+50
76+
The current rounding mode is: fe_dec_downward
77+
Sum with downward rounding: 4.999999e+50
78+
....
79+
====
80+
9681
As shown, changing the rounding mode *WILL* change your numerical results.
9782
If you are coming from the Intel library (or other C-style libs) where every mathematical function takes a rounding mode, that is not the case in this library; the only way to change the rounding mode for individual operations is via the global rounding mode.
9883
Before attempting to change the rounding mode ensure this is actually what you want to happen.
9984

10085
You can similarly change the default rounding mode at compile time with *ANY* compiler (unlike at runtime) using similarly named macros:
10186

102-
1. BOOST_DECIMAL_FE_DEC_DOWNWARD
103-
2. BOOST_DECIMAL_FE_DEC_TO_NEAREST
104-
3. BOOST_DECIMAL_FE_DEC_TO_NEAREST_FROM_ZERO
105-
4. BOOST_DECIMAL_FE_DEC_TOWARD_ZERO
106-
5. BOOST_DECIMAL_FE_DEC_UPWARD
87+
1. `BOOST_DECIMAL_FE_DEC_DOWNWARD`
88+
2. `BOOST_DECIMAL_FE_DEC_TO_NEAREST`
89+
3. `BOOST_DECIMAL_FE_DEC_TO_NEAREST_FROM_ZERO`
90+
4. `BOOST_DECIMAL_FE_DEC_TOWARD_ZERO`
91+
5. `BOOST_DECIMAL_FE_DEC_UPWARD`
10792

108-
If none of the above macros are defined, the default rounding mode for compile time is the same as the default runtime (i.e. as if BOOST_DECIMAL_FE_DEC_TO_NEAREST were defined).
93+
If none of the above macros are defined, the default rounding mode for compile time is the same as the default runtime (i.e. as if `BOOST_DECIMAL_FE_DEC_TO_NEAREST` were defined).
10994
At most *ONE* of these macros are allowed to be user defined.
11095
A `#error` will be emitted if more than one is detected.
11196
The macro must be defined before the inclusion of any decimal library header.
@@ -117,22 +102,18 @@ This same example can be reduced for the cases where:
117102

118103
2. You want to change the compile time rounding mode
119104

105+
.This https://github.com/cppalliance/decimal/blob/develop/examples/rounding_mode.cpp[example] demonstrates how the compile time rounding mode can be set, and it's effects on compile time and run time evaluation.
106+
====
120107
[source, c++]
121108
----
122-
#define BOOST_DECIMAL_FE_DEC_DOWNWARD
123-
#include <boost/decimal.hpp>
124-
125-
int main()
126-
{
127-
using namespace boost::decimal::literals;
128-
129-
constexpr auto lhs {"5e+50"_DF};
130-
constexpr auto rhs {"4e+40"_DF};
131-
constexpr auto downward_res {lhs - rhs};
132-
static_assert(downward_res == "4.999999e+50"_DF, "Incorrectly rounded result");
133-
134-
return 0;
135-
}
109+
include::example$rounding_mode_compile_time.cpp[]
136110
----
137111
112+
.Expected Output
113+
....
114+
The default rounding mode is: fe_dec_to_nearest
115+
The current rounding mode is: fe_dec_downward
116+
....
117+
====
118+
138119
IMPORTANT: Prior to v5.2.0 this header was `<boost/decimal/fenv.hpp>`, but has been changed to `<boost/decimal/cfenv.hpp>` for consistency with the STL naming convention.

examples/first_example.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/decimal.hpp> // This includes the entire decimal library
6+
#include <iostream>
7+
#include <iomanip>
8+
9+
int main()
10+
{
11+
using namespace boost::decimal::literals; // The literals are in their own namespace like std::literals
12+
13+
// First we show the result of 0.1 + 0.2 using regular doubles
14+
std::cout << std::fixed << std::setprecision(17)
15+
<< "Using doubles:\n"
16+
<< "0.1 + 0.2 = " << 0.1 + 0.2 << "\n\n";
17+
18+
// Construct the two decimal values
19+
// We construct using the literals defined by the library
20+
constexpr boost::decimal::decimal64_t a {0.1_DD};
21+
constexpr boost::decimal::decimal64_t b {0.2_DD};
22+
23+
// Now we display the result of the same calculation using decimal64_t
24+
std::cout << "Using decimal64_t:\n"
25+
<< "0.1_DD + 0.2_DD = " << a + b << std::endl;
26+
}

examples/rounding_mode.cpp

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,87 @@
1-
// Copyright 2024 Matt Borland
1+
// Copyright 2024 - 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 example demonstrates how to set and get the global rounding mode
6+
// as well as the effects on numerical results
47

5-
#include <boost/decimal.hpp>
6-
#include <cassert>
8+
#include <boost/decimal/decimal32_t.hpp> // For type decimal32_t
9+
#include <boost/decimal/literals.hpp> // For decimal literals
10+
#include <boost/decimal/cfenv.hpp> // For access to the rounding mode functions
11+
#include <boost/decimal/iostream.hpp> // Decimal support to <iostream>
12+
#include <iostream>
13+
14+
void print_rounding_mode(const boost::decimal::rounding_mode current_mode)
15+
{
16+
// All 5 rounding modes are defined by the enum rounding_mode
17+
using boost::decimal::rounding_mode;
18+
19+
switch (current_mode)
20+
{
21+
case rounding_mode::fe_dec_downward:
22+
std::cout << "fe_dec_downward\n";
23+
break;
24+
case rounding_mode::fe_dec_to_nearest:
25+
std::cout << "fe_dec_to_nearest\n";
26+
break;
27+
case rounding_mode::fe_dec_to_nearest_from_zero:
28+
std::cout << "fe_dec_to_nearest_from_zero\n";
29+
break;
30+
case rounding_mode::fe_dec_toward_zero:
31+
std::cout << "fe_dec_toward_zero\n";
32+
break;
33+
case rounding_mode::fe_dec_upward:
34+
std::cout << "fe_dec_upward\n";
35+
break;
36+
}
37+
}
738

839
int main()
940
{
10-
#ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
41+
// The rounding mode can only be changed at run-time if the compiler supports
42+
// 1. C++20 std::is_constant_evaluated()
43+
// 2. Intrinsics that do the same
44+
// If neither of the above are defined the library defines BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
1145

12-
using namespace boost::decimal::literals;
46+
// The current rounding mode can be queried with boost::decimal::fegetround
47+
const boost::decimal::rounding_mode default_rounding_mode = boost::decimal::fegetround();
48+
std::cout << "The default rounding mode is: ";
49+
print_rounding_mode(default_rounding_mode);
1350

14-
BOOST_DECIMAL_ATTRIBUTE_UNUSED auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest
51+
// To set a new rounding mode use boost::decimal::fesetround
52+
// fesetround returns current mode after updating the global state
53+
//
54+
// If your compiler set defines BOOST_DECIMAL_NO_CONSTEVAL_DETECTION the global state can not be updated,
55+
// so this can be a useful check to make sure that state is what you expect it to be
56+
auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_upward);
57+
std::cout << "The current rounding mode is: ";
58+
print_rounding_mode(new_rounding_mode);
1559

16-
BOOST_DECIMAL_ATTRIBUTE_UNUSED auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_upward);
60+
#ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
61+
62+
using namespace boost::decimal::literals;
63+
using boost::decimal::decimal32_t;
1764

18-
assert(default_rounding_mode != new_rounding_mode);
65+
const decimal32_t lhs {"5e+50"_DF};
66+
const decimal32_t rhs {"4e+40"_DF};
1967

20-
const auto lhs {"5e+50"_DF};
21-
const auto rhs {"4e+40"_DF};
68+
std::cout << "lhs equals: " << lhs << '\n'
69+
<< "rhs equals: " << rhs << '\n';
2270

2371
// With upward rounding the result will be "5.000001e+50"_DF
2472
// Even though the difference in order of magnitude is greater than the precision of the type,
2573
// any addition in this mode will result in at least a one ULP difference
26-
BOOST_DECIMAL_ATTRIBUTE_UNUSED const auto upward_res {lhs + rhs};
27-
assert(upward_res == "5.000001e+50"_DF);
74+
const decimal32_t upward_res {lhs + rhs};
75+
std::cout << " Sum with upward rounding: " << upward_res << '\n';
2876

29-
boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_downward);
77+
78+
new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_downward);
79+
std::cout << "The current rounding mode is: ";
80+
print_rounding_mode(new_rounding_mode);
3081

3182
// Similar to above in the downward rounding mode any subtraction will result in at least a one ULP difference
32-
BOOST_DECIMAL_ATTRIBUTE_UNUSED const auto downward_res {lhs - rhs};
33-
assert(downward_res == "4.999999e+50"_DF);
83+
const decimal32_t downward_res {lhs - rhs};
84+
std::cout << "Sum with downward rounding: " << downward_res << '\n';
3485

3586
#endif // BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
36-
37-
return 0;
3887
}
39-
Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,63 @@
11
// Copyright 2024 Matt Borland
22
// Distributed under the Boost Software License, Version 1.0.
33
// https://www.boost.org/LICENSE_1_0.txt
4-
4+
//
5+
// To define a global compile-time rounding mode
6+
// you must define the macro before inclusion of *ANY* decimal header
57
#define BOOST_DECIMAL_FE_DEC_DOWNWARD
6-
#include <boost/decimal.hpp>
8+
9+
#include <boost/decimal/decimal32_t.hpp> // For type decimal32_t
10+
#include <boost/decimal/literals.hpp> // For decimal literals
11+
#include <boost/decimal/iostream.hpp> // For decimal <iostream> support
12+
#include <boost/decimal/cfenv.hpp> // For rounding mode access
13+
#include <iostream>
14+
15+
void print_rounding_mode(const boost::decimal::rounding_mode current_mode)
16+
{
17+
// All 5 rounding modes are defined by the enum rounding_mode
18+
using boost::decimal::rounding_mode;
19+
20+
switch (current_mode)
21+
{
22+
case rounding_mode::fe_dec_downward:
23+
std::cout << "fe_dec_downward\n";
24+
break;
25+
case rounding_mode::fe_dec_to_nearest:
26+
std::cout << "fe_dec_to_nearest\n";
27+
break;
28+
case rounding_mode::fe_dec_to_nearest_from_zero:
29+
std::cout << "fe_dec_to_nearest_from_zero\n";
30+
break;
31+
case rounding_mode::fe_dec_toward_zero:
32+
std::cout << "fe_dec_toward_zero\n";
33+
break;
34+
case rounding_mode::fe_dec_upward:
35+
std::cout << "fe_dec_upward\n";
36+
break;
37+
}
38+
}
739

840
int main()
941
{
1042
using namespace boost::decimal::literals;
43+
using boost::decimal::decimal32_t;
1144

12-
constexpr auto lhs {"5e+50"_DF};
13-
constexpr auto rhs {"4e+40"_DF};
14-
constexpr auto downward_res {lhs - rhs};
45+
// This uses one of the same examples from our runtime rounding mode example
46+
// Now we can see the effects on the generation of constants,
47+
// since we can static_assert the result
48+
49+
constexpr decimal32_t lhs {"5e+50"_DF};
50+
constexpr decimal32_t rhs {"4e+40"_DF};
51+
constexpr decimal32_t downward_res {lhs - rhs};
1552
static_assert(downward_res == "4.999999e+50"_DF, "Incorrectly rounded result");
1653

17-
return 0;
54+
std::cout << "The default rounding mode is: ";
55+
print_rounding_mode(boost::decimal::rounding_mode::fe_dec_default);
56+
57+
// Here we can see that the rounding mode has been set to something besides default
58+
// without having had to call fesetround
59+
//
60+
// This works with all compilers unlike changing the rounding mode at run-time
61+
std::cout << "The current rounding mode is: ";
62+
print_rounding_mode(boost::decimal::fegetround());
1863
}

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ run ../examples/fmt_format.cpp ;
205205
run ../examples/print.cpp ;
206206
run ../examples/promotion.cpp ;
207207
run ../examples/numerical_parsing.cpp ;
208+
run ../examples/first_example.cpp ;
208209

209210
# Test compilation of separate headers
210211
compile compile_tests/bid_conversion.cpp ;

0 commit comments

Comments
 (0)