Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
** xref:examples.adoc#examples_charconv[`<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]
Expand Down
52 changes: 52 additions & 0 deletions doc/modules/ROOT/pages/conversions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 <boost/decimal/decimal32_t.hpp>
#include <boost/decimal/dpd_conversion.hpp>
#include <boost/decimal/bid_conversion.hpp>
#include <iostream>

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<decimal32_t>(BID_bits)};
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(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
----
81 changes: 26 additions & 55 deletions doc/modules/ROOT/pages/examples.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <boost/decimal.hpp>
#include <limits>
#include <cmath>

int error_counter = 0;

template <typename T>
bool float_equal(T lhs, T rhs)
{
using std::fabs;
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
}

template <typename T>
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();
}
----

Expand Down Expand Up @@ -173,38 +176,6 @@ int main()
}
----

[#examples_bit_conversions]
== Bit Conversions
[source, c++]
----
#include <boost/decimal.hpp>
#include <iostream>
#include <iomanip>

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<decimal32_t>(BID_bits)};
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(DPD_bits)};

return !(bid_decimal == dpd_decimal);
}
----
Output:
----
BID format: 31fc4b40
DPD format: 35f00000
----

[#examples_finance]
== Financial Applications

Expand Down
53 changes: 28 additions & 25 deletions examples/adl.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/decimal.hpp>
#include <limits>
#include <cmath>

int error_counter = 0;

template <typename T>
bool float_equal(T lhs, T rhs)
void sin_identity(T val)
{
using std::fabs;
return fabs(lhs - rhs) < std::numeric_limits<T>::epsilon(); // numeric_limits is overloaded for all decimal types
}

template <typename T>
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();
}


33 changes: 25 additions & 8 deletions examples/bit_conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/decimal.hpp>
#include <boost/decimal/decimal32_t.hpp>
#include <boost/decimal/dpd_conversion.hpp>
#include <boost/decimal/bid_conversion.hpp>
#include <iostream>
#include <iomanip>

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<decimal32_t>(BID_bits)};
const decimal32_t dpd_decimal {from_dpd<decimal32_t>(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;
}
}
63 changes: 63 additions & 0 deletions examples/test.hpp
Original file line number Diff line number Diff line change
@@ -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 <boost/decimal/iostream.hpp>
#include <iostream>
#include <type_traits>
#include <limits>

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 <typename T, std::enable_if_t<!std::is_floating_point<T>::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 <typename T, std::enable_if_t<std::is_floating_point<T>::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<T>::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