diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index daf782869..a1dfdec64 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -6,7 +6,6 @@ ** xref:examples.adoc#examples_charconv[``] ** xref:examples.adoc#examples_generic_programming[Generic Programming] ** xref:examples.adoc#examples_literals_constants[Literals and Constants] -** xref:examples.adoc#examples_bit_conversions[Bit Conversions] ** xref:examples.adoc#examples_finance[Financial Applications] ** xref:examples.adoc#examples_boost_math[Boost.Math Integration] ** xref:examples.adoc#examples_format[Formatting] diff --git a/doc/modules/ROOT/pages/conversions.adoc b/doc/modules/ROOT/pages/conversions.adoc index c81512fe3..1cbf5d2f5 100644 --- a/doc/modules/ROOT/pages/conversions.adoc +++ b/doc/modules/ROOT/pages/conversions.adoc @@ -9,6 +9,7 @@ https://www.boost.org/LICENSE_1_0.txt :idprefix: conversions_ IEEE 754 specifies two different encodings for decimal floating point types: Binary Integer Significand Field (BID), and Densely Packed Decimal Significand Field (DPD). +The former is designed for software implementations and the latter for hardware implementations. Internally this library is implemented in the BID format for the IEEE-754 compliant types. 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. @@ -114,3 +115,54 @@ constexpr T from_dpd(unsigned __int128 bits) noexcept; } // namespace decimal } // namespace boost ---- + +== Bit Conversions Example + +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]. + +[source, c++] +---- +#include +#include +#include +#include + +int main() +{ + using boost::decimal::decimal32_t; + using boost::decimal::from_bid; + using boost::decimal::from_dpd; + using boost::decimal::to_bid; + using boost::decimal::to_dpd; + + // First we construct a decimal value and then convert it into both BID and DPD encoded bits + const decimal32_t decimal_value {5}; + const std::uint32_t BID_bits {to_bid(decimal_value)}; + const std::uint32_t DPD_bits {to_dpd(decimal_value)}; + + // Display the difference between the hex values of both bit encodings + std::cout << std::hex + << "BID format: " << BID_bits << '\n' + << "DPD format: " << DPD_bits << std::endl; + + // Recover the original value by encoding two new decimals using the from_bid and from_dpd functions + const decimal32_t bid_decimal {from_bid(BID_bits)}; + const decimal32_t dpd_decimal {from_dpd(DPD_bits)}; + + if (bid_decimal == dpd_decimal) + { + // These should both have recovered the original value of 5 + return 0; + } + else + { + // Something has gone wrong recovering the decimal values + return 1; + } +} +---- +Output: +---- +BID format: 32800005 +DPD format: 22500005 +---- diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index e617ae805..bcd1a0850 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -103,42 +103,45 @@ Returned Value: 0.25 [#examples_generic_programming] == Generic Programming + +This example can be found in the `examples/` folder as https://github.com/cppalliance/decimal/blob/develop/examples/adl.cpp[adl.cpp]. + [source, c++] ---- +#include "test.hpp" #include -#include #include -int error_counter = 0; - -template -bool float_equal(T lhs, T rhs) -{ - using std::fabs; - return fabs(lhs - rhs) < std::numeric_limits::epsilon(); // numeric_limits is overloaded for all decimal types -} - template -void test(T val) +void sin_identity(T val) { - using std::sin; // ADL allows builtin and decimal types to both be used - if (!float_equal(sin(val), -sin(-val))) // sin(x) == -sin(-x) - { - ++error_counter; - } + // ADL allows builtin and decimal types to both be used + // Boost.Decimal is not allowed to overload std::sin so it must be provided in its own namespace + // You must also include using std::sin to ensure that it is found for the float, double, and long double cases. + // It is preferred to have using statements for the functions you intend to use instead of using namespace XXX. + using std::sin; + using boost::decimal::sin; + + // sin(x) = -sin(-x) + // The call here MUST be unqualified, or you will get compiler errors + // For example calling std::sin here would not allow any of the decimal types to be used + BOOST_DECIMAL_TEST_EQ(sin(val), -sin(-val)); } int main() { - test(-0.5F); - test(-0.5); - test(-0.5L); + // Because of the two using statements in the above function we can now call it with built-in floating point, + // or our decimal types as show below + + sin_identity(-0.5F); + sin_identity(-0.5); + sin_identity(-0.5L); - test(boost::decimal::decimal32_t{5, -1, true}); - test(boost::decimal::decimal64_t{5, -1, true}); - test(boost::decimal::decimal128_t{5, -1, true}); + sin_identity(boost::decimal::decimal32_t{"-0.5"}); + sin_identity(boost::decimal::decimal64_t{"-0.5"}); + sin_identity(boost::decimal::decimal128_t{"-0.5"}); - return error_counter; + return boost::decimal::test::report_errors(); } ---- @@ -173,38 +176,6 @@ int main() } ---- -[#examples_bit_conversions] -== Bit Conversions -[source, c++] ----- -#include -#include -#include - -using namespace boost::decimal; - -int main() -{ - const decimal_fast32_t fast_type {5}; - const std::uint32_t BID_bits {to_bid(fast_type)}; - const std::uint32_t DPD_bits {to_dpd(fast_type)}; - - std::cout << std::hex - << "BID format: " << BID_bits << '\n' - << "DPD format: " << DPD_bits << std::endl; - - const decimal32_t bid_decimal {from_bid(BID_bits)}; - const decimal32_t dpd_decimal {from_dpd(DPD_bits)}; - - return !(bid_decimal == dpd_decimal); -} ----- -Output: ----- -BID format: 31fc4b40 -DPD format: 35f00000 ----- - [#examples_finance] == Financial Applications diff --git a/examples/adl.cpp b/examples/adl.cpp index 1ad40a0c6..a192b7e27 100644 --- a/examples/adl.cpp +++ b/examples/adl.cpp @@ -1,41 +1,44 @@ // Copyright 2024 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +// +// This example shows how we are able to use adl with Boost.Decimal to allow a template function +// to use both built-in binary floating point types, as well as Boost.Decimal types +#include "test.hpp" #include -#include #include -int error_counter = 0; - template -bool float_equal(T lhs, T rhs) +void sin_identity(T val) { - using std::fabs; - return fabs(lhs - rhs) < std::numeric_limits::epsilon(); // numeric_limits is overloaded for all decimal types -} - -template -void test(T val) -{ - using std::sin; // ADL allows builtin and decimal types to both be used - if (!float_equal(sin(val), -sin(-val))) // sin(x) == -sin(-x) - { - ++error_counter; - } + // ADL allows builtin and decimal types to both be used + // Boost.Decimal is not allowed to overload std::sin so it must be provided in its own namespace + // You must also include using std::sin to ensure that it is found for the float, double, and long double cases. + // It is preferred to have using statements for the functions you intend to use instead of using namespace XXX. + using std::sin; + using boost::decimal::sin; + + // sin(x) = -sin(-x) + // The call here MUST be unqualified, or you will get compiler errors + // For example calling std::sin here would not allow any of the decimal types to be used + BOOST_DECIMAL_TEST_EQ(sin(val), -sin(-val)); } int main() { - test(-0.5F); - test(-0.5); - test(-0.5L); - - test(boost::decimal::decimal32_t{-5, -1}); - test(boost::decimal::decimal64_t{-5, -1}); - test(boost::decimal::decimal128_t{-5, -1}); - - return error_counter; + // Because of the two using statements in the above function we can now call it with built-in floating point, + // or our decimal types as show below + + sin_identity(-0.5F); + sin_identity(-0.5); + sin_identity(-0.5L); + + sin_identity(boost::decimal::decimal32_t{"-0.5"}); + sin_identity(boost::decimal::decimal64_t{"-0.5"}); + sin_identity(boost::decimal::decimal128_t{"-0.5"}); + + return boost::decimal::test::report_errors(); } diff --git a/examples/bit_conversions.cpp b/examples/bit_conversions.cpp index 86a15863f..a40d496b1 100644 --- a/examples/bit_conversions.cpp +++ b/examples/bit_conversions.cpp @@ -2,24 +2,41 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include +#include +#include +#include #include -#include - -using namespace boost::decimal; int main() { - const decimal_fast32_t fast_type {5}; - const std::uint32_t BID_bits {to_bid(fast_type)}; - const std::uint32_t DPD_bits {to_dpd(fast_type)}; + using boost::decimal::decimal32_t; + using boost::decimal::from_bid; + using boost::decimal::from_dpd; + using boost::decimal::to_bid; + using boost::decimal::to_dpd; + + // First we construct a decimal value and then convert it into both BID and DPD encoded bits + const decimal32_t decimal_value {5}; + const std::uint32_t BID_bits {to_bid(decimal_value)}; + const std::uint32_t DPD_bits {to_dpd(decimal_value)}; + // Display the difference between the hex values of both bit encodings std::cout << std::hex << "BID format: " << BID_bits << '\n' << "DPD format: " << DPD_bits << std::endl; + // Recover the original value by encoding two new decimals using the from_bid and from_dpd functions const decimal32_t bid_decimal {from_bid(BID_bits)}; const decimal32_t dpd_decimal {from_dpd(DPD_bits)}; - return !(bid_decimal == dpd_decimal); + if (bid_decimal == dpd_decimal) + { + // These should both have recovered the original value of 5 + return 0; + } + else + { + // Something has gone wrong recovering the decimal values + return 1; + } } diff --git a/examples/test.hpp b/examples/test.hpp new file mode 100644 index 000000000..5a6b28ed7 --- /dev/null +++ b/examples/test.hpp @@ -0,0 +1,63 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// Some trivial testing facilities so that the library examples can run without boost +// Running the full test suite in the test/ directory does require boost + +#ifndef BOOST_DECIMAL_EXAMPLES_TEST_HPP +#define BOOST_DECIMAL_EXAMPLES_TEST_HPP + +#include +#include +#include +#include + +namespace boost { +namespace decimal { +namespace test { + +static int errors = 0; + +inline int report_errors() +{ + // Limited on the upper bound of main return on some platforms + return errors > 255 ? 255 : errors; +} + +inline void test(const bool x) +{ + if (!x) + { + ++errors; + std::cerr << "Test Failed at line: " << __LINE__ << std::endl; + } +} + +template ::value, bool> = true> +void test_eq(const T lhs, const T rhs) +{ + if (lhs != rhs) + { + ++errors; + std::cerr << "Failed equality test for: " << lhs << " and " << rhs << " at line: " << __LINE__ << std::endl; + } +} + +template ::value, bool> = true> +void test_eq(const T a, const T b) +{ + // Knuth's approximate float equality from The Art of Computer Programming + // See also: https://stackoverflow.com/questions/17333/how-do-you-compare-float-and-double-while-accounting-for-precision-loss + using std::fabs; + test(fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits::epsilon())); +} + +} // namespace test +} // namespace decimal +} // namespace boost + +#define BOOST_DECIMAL_TEST(x) boost::decimal::test::test(x); +#define BOOST_DECIMAL_TEST_EQ(lhs, rhs) boost::decimal::test::test_eq(lhs, rhs) + +#endif // BOOST_DECIMAL_EXAMPLES_TEST_HPP