Skip to content

Commit 53b2c7b

Browse files
authored
Merge pull request #1221 from cppalliance/example_testing
Improve ADL and bit conversions examples as well as re-locate the latter
2 parents 0bc45a2 + 1d12331 commit 53b2c7b

File tree

6 files changed

+194
-89
lines changed

6 files changed

+194
-89
lines changed

doc/modules/ROOT/nav.adoc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
** xref:examples.adoc#examples_charconv[`<charconv>`]
77
** xref:examples.adoc#examples_generic_programming[Generic Programming]
88
** xref:examples.adoc#examples_literals_constants[Literals and Constants]
9-
** xref:examples.adoc#examples_bit_conversions[Bit Conversions]
109
** xref:examples.adoc#examples_finance[Financial Applications]
1110
** xref:examples.adoc#examples_boost_math[Boost.Math Integration]
1211
** xref:examples.adoc#examples_format[Formatting]

doc/modules/ROOT/pages/conversions.adoc

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

1111
IEEE 754 specifies two different encodings for decimal floating point types: Binary Integer Significand Field (BID), and Densely Packed Decimal Significand Field (DPD).
12+
The former is designed for software implementations and the latter for hardware implementations.
1213
Internally this library is implemented in the BID format for the IEEE-754 compliant types.
1314
Should the user want to capture the bit format in BID or convert to DPD we offer a family of conversion functions: `to_bid`, `from_bid`, `to_dpd`, and `from_dpd` that allow conversion to or from the bit strings regardless of encoding.
1415

@@ -114,3 +115,54 @@ constexpr T from_dpd(unsigned __int128 bits) noexcept;
114115
} // namespace decimal
115116
} // namespace boost
116117
----
118+
119+
== Bit Conversions Example
120+
121+
The following example is copied and pasted from the examples/ folder of the library and is called https://github.com/cppalliance/decimal/blob/develop/examples/bit_conversions.cpp[bit_conversions.cpp].
122+
123+
[source, c++]
124+
----
125+
#include <boost/decimal/decimal32_t.hpp>
126+
#include <boost/decimal/dpd_conversion.hpp>
127+
#include <boost/decimal/bid_conversion.hpp>
128+
#include <iostream>
129+
130+
int main()
131+
{
132+
using boost::decimal::decimal32_t;
133+
using boost::decimal::from_bid;
134+
using boost::decimal::from_dpd;
135+
using boost::decimal::to_bid;
136+
using boost::decimal::to_dpd;
137+
138+
// First we construct a decimal value and then convert it into both BID and DPD encoded bits
139+
const decimal32_t decimal_value {5};
140+
const std::uint32_t BID_bits {to_bid(decimal_value)};
141+
const std::uint32_t DPD_bits {to_dpd(decimal_value)};
142+
143+
// Display the difference between the hex values of both bit encodings
144+
std::cout << std::hex
145+
<< "BID format: " << BID_bits << '\n'
146+
<< "DPD format: " << DPD_bits << std::endl;
147+
148+
// Recover the original value by encoding two new decimals using the from_bid and from_dpd functions
149+
const decimal32_t bid_decimal {from_bid<decimal32_t>(BID_bits)};
150+
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(DPD_bits)};
151+
152+
if (bid_decimal == dpd_decimal)
153+
{
154+
// These should both have recovered the original value of 5
155+
return 0;
156+
}
157+
else
158+
{
159+
// Something has gone wrong recovering the decimal values
160+
return 1;
161+
}
162+
}
163+
----
164+
Output:
165+
----
166+
BID format: 32800005
167+
DPD format: 22500005
168+
----

doc/modules/ROOT/pages/examples.adoc

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -103,42 +103,45 @@ Returned Value: 0.25
103103
104104
[#examples_generic_programming]
105105
== Generic Programming
106+
107+
This example can be found in the `examples/` folder as https://github.com/cppalliance/decimal/blob/develop/examples/adl.cpp[adl.cpp].
108+
106109
[source, c++]
107110
----
111+
#include "test.hpp"
108112
#include <boost/decimal.hpp>
109-
#include <limits>
110113
#include <cmath>
111114

112-
int error_counter = 0;
113-
114-
template <typename T>
115-
bool float_equal(T lhs, T rhs)
116-
{
117-
using std::fabs;
118-
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
119-
}
120-
121115
template <typename T>
122-
void test(T val)
116+
void sin_identity(T val)
123117
{
124-
using std::sin; // ADL allows builtin and decimal types to both be used
125-
if (!float_equal(sin(val), -sin(-val))) // sin(x) == -sin(-x)
126-
{
127-
++error_counter;
128-
}
118+
// ADL allows builtin and decimal types to both be used
119+
// Boost.Decimal is not allowed to overload std::sin so it must be provided in its own namespace
120+
// You must also include using std::sin to ensure that it is found for the float, double, and long double cases.
121+
// It is preferred to have using statements for the functions you intend to use instead of using namespace XXX.
122+
using std::sin;
123+
using boost::decimal::sin;
124+
125+
// sin(x) = -sin(-x)
126+
// The call here MUST be unqualified, or you will get compiler errors
127+
// For example calling std::sin here would not allow any of the decimal types to be used
128+
BOOST_DECIMAL_TEST_EQ(sin(val), -sin(-val));
129129
}
130130

131131
int main()
132132
{
133-
test(-0.5F);
134-
test(-0.5);
135-
test(-0.5L);
133+
// Because of the two using statements in the above function we can now call it with built-in floating point,
134+
// or our decimal types as show below
135+
136+
sin_identity(-0.5F);
137+
sin_identity(-0.5);
138+
sin_identity(-0.5L);
136139

137-
test(boost::decimal::decimal32_t{5, -1, true});
138-
test(boost::decimal::decimal64_t{5, -1, true});
139-
test(boost::decimal::decimal128_t{5, -1, true});
140+
sin_identity(boost::decimal::decimal32_t{"-0.5"});
141+
sin_identity(boost::decimal::decimal64_t{"-0.5"});
142+
sin_identity(boost::decimal::decimal128_t{"-0.5"});
140143

141-
return error_counter;
144+
return boost::decimal::test::report_errors();
142145
}
143146
----
144147
@@ -173,38 +176,6 @@ int main()
173176
}
174177
----
175178
176-
[#examples_bit_conversions]
177-
== Bit Conversions
178-
[source, c++]
179-
----
180-
#include <boost/decimal.hpp>
181-
#include <iostream>
182-
#include <iomanip>
183-
184-
using namespace boost::decimal;
185-
186-
int main()
187-
{
188-
const decimal_fast32_t fast_type {5};
189-
const std::uint32_t BID_bits {to_bid(fast_type)};
190-
const std::uint32_t DPD_bits {to_dpd(fast_type)};
191-
192-
std::cout << std::hex
193-
<< "BID format: " << BID_bits << '\n'
194-
<< "DPD format: " << DPD_bits << std::endl;
195-
196-
const decimal32_t bid_decimal {from_bid<decimal32_t>(BID_bits)};
197-
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(DPD_bits)};
198-
199-
return !(bid_decimal == dpd_decimal);
200-
}
201-
----
202-
Output:
203-
----
204-
BID format: 31fc4b40
205-
DPD format: 35f00000
206-
----
207-
208179
[#examples_finance]
209180
== Financial Applications
210181

examples/adl.cpp

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,44 @@
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+
//
5+
// This example shows how we are able to use adl with Boost.Decimal to allow a template function
6+
// to use both built-in binary floating point types, as well as Boost.Decimal types
47

8+
#include "test.hpp"
59
#include <boost/decimal.hpp>
6-
#include <limits>
710
#include <cmath>
811

9-
int error_counter = 0;
10-
1112
template <typename T>
12-
bool float_equal(T lhs, T rhs)
13+
void sin_identity(T val)
1314
{
14-
using std::fabs;
15-
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
16-
}
17-
18-
template <typename T>
19-
void test(T val)
20-
{
21-
using std::sin; // ADL allows builtin and decimal types to both be used
22-
if (!float_equal(sin(val), -sin(-val))) // sin(x) == -sin(-x)
23-
{
24-
++error_counter;
25-
}
15+
// ADL allows builtin and decimal types to both be used
16+
// Boost.Decimal is not allowed to overload std::sin so it must be provided in its own namespace
17+
// You must also include using std::sin to ensure that it is found for the float, double, and long double cases.
18+
// It is preferred to have using statements for the functions you intend to use instead of using namespace XXX.
19+
using std::sin;
20+
using boost::decimal::sin;
21+
22+
// sin(x) = -sin(-x)
23+
// The call here MUST be unqualified, or you will get compiler errors
24+
// For example calling std::sin here would not allow any of the decimal types to be used
25+
BOOST_DECIMAL_TEST_EQ(sin(val), -sin(-val));
2626
}
2727

2828
int main()
2929
{
30-
test(-0.5F);
31-
test(-0.5);
32-
test(-0.5L);
33-
34-
test(boost::decimal::decimal32_t{-5, -1});
35-
test(boost::decimal::decimal64_t{-5, -1});
36-
test(boost::decimal::decimal128_t{-5, -1});
37-
38-
return error_counter;
30+
// Because of the two using statements in the above function we can now call it with built-in floating point,
31+
// or our decimal types as show below
32+
33+
sin_identity(-0.5F);
34+
sin_identity(-0.5);
35+
sin_identity(-0.5L);
36+
37+
sin_identity(boost::decimal::decimal32_t{"-0.5"});
38+
sin_identity(boost::decimal::decimal64_t{"-0.5"});
39+
sin_identity(boost::decimal::decimal128_t{"-0.5"});
40+
41+
return boost::decimal::test::report_errors();
3942
}
4043

4144

examples/bit_conversions.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,41 @@
22
// Distributed under the Boost Software License, Version 1.0.
33
// https://www.boost.org/LICENSE_1_0.txt
44

5-
#include <boost/decimal.hpp>
5+
#include <boost/decimal/decimal32_t.hpp>
6+
#include <boost/decimal/dpd_conversion.hpp>
7+
#include <boost/decimal/bid_conversion.hpp>
68
#include <iostream>
7-
#include <iomanip>
8-
9-
using namespace boost::decimal;
109

1110
int main()
1211
{
13-
const decimal_fast32_t fast_type {5};
14-
const std::uint32_t BID_bits {to_bid(fast_type)};
15-
const std::uint32_t DPD_bits {to_dpd(fast_type)};
12+
using boost::decimal::decimal32_t;
13+
using boost::decimal::from_bid;
14+
using boost::decimal::from_dpd;
15+
using boost::decimal::to_bid;
16+
using boost::decimal::to_dpd;
17+
18+
// First we construct a decimal value and then convert it into both BID and DPD encoded bits
19+
const decimal32_t decimal_value {5};
20+
const std::uint32_t BID_bits {to_bid(decimal_value)};
21+
const std::uint32_t DPD_bits {to_dpd(decimal_value)};
1622

23+
// Display the difference between the hex values of both bit encodings
1724
std::cout << std::hex
1825
<< "BID format: " << BID_bits << '\n'
1926
<< "DPD format: " << DPD_bits << std::endl;
2027

28+
// Recover the original value by encoding two new decimals using the from_bid and from_dpd functions
2129
const decimal32_t bid_decimal {from_bid<decimal32_t>(BID_bits)};
2230
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(DPD_bits)};
2331

24-
return !(bid_decimal == dpd_decimal);
32+
if (bid_decimal == dpd_decimal)
33+
{
34+
// These should both have recovered the original value of 5
35+
return 0;
36+
}
37+
else
38+
{
39+
// Something has gone wrong recovering the decimal values
40+
return 1;
41+
}
2542
}

examples/test.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
// Some trivial testing facilities so that the library examples can run without boost
6+
// Running the full test suite in the test/ directory does require boost
7+
8+
#ifndef BOOST_DECIMAL_EXAMPLES_TEST_HPP
9+
#define BOOST_DECIMAL_EXAMPLES_TEST_HPP
10+
11+
#include <boost/decimal/iostream.hpp>
12+
#include <iostream>
13+
#include <type_traits>
14+
#include <limits>
15+
16+
namespace boost {
17+
namespace decimal {
18+
namespace test {
19+
20+
static int errors = 0;
21+
22+
inline int report_errors()
23+
{
24+
// Limited on the upper bound of main return on some platforms
25+
return errors > 255 ? 255 : errors;
26+
}
27+
28+
inline void test(const bool x)
29+
{
30+
if (!x)
31+
{
32+
++errors;
33+
std::cerr << "Test Failed at line: " << __LINE__ << std::endl;
34+
}
35+
}
36+
37+
template <typename T, std::enable_if_t<!std::is_floating_point<T>::value, bool> = true>
38+
void test_eq(const T lhs, const T rhs)
39+
{
40+
if (lhs != rhs)
41+
{
42+
++errors;
43+
std::cerr << "Failed equality test for: " << lhs << " and " << rhs << " at line: " << __LINE__ << std::endl;
44+
}
45+
}
46+
47+
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
48+
void test_eq(const T a, const T b)
49+
{
50+
// Knuth's approximate float equality from The Art of Computer Programming
51+
// See also: https://stackoverflow.com/questions/17333/how-do-you-compare-float-and-double-while-accounting-for-precision-loss
52+
using std::fabs;
53+
test(fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits<T>::epsilon()));
54+
}
55+
56+
} // namespace test
57+
} // namespace decimal
58+
} // namespace boost
59+
60+
#define BOOST_DECIMAL_TEST(x) boost::decimal::test::test(x);
61+
#define BOOST_DECIMAL_TEST_EQ(lhs, rhs) boost::decimal::test::test_eq(lhs, rhs)
62+
63+
#endif // BOOST_DECIMAL_EXAMPLES_TEST_HPP

0 commit comments

Comments
 (0)