Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Using without expression templates for Boost.Test and others

As noted in the Boost.Multiprecision documentation, certain program constructs will not compile when using expression templates. One example that many users may encounter is Boost.Test (1.54 and earlier) when using macro BOOST_CHECK_CLOSE and BOOST_CHECK_CLOSE_FRACTION.

If, for example, you wish to use any multiprecision type like cpp_dec_float_50 in place of double to give more precision, you will need to override the default boost::multiprecision::et_on with boost::multiprecision::et_off.

#include <boost/multiprecision/cpp_dec_float.hpp>

To define a 50 decimal digit type using cpp_dec_float, you must pass two template parameters to boost::multiprecision::number.

It may be more legible to use a two-staged type definition such as this:

typedef boost::multiprecision::cpp_dec_float<50> mp_backend;
typedef boost::multiprecision::number<mp_backend, boost::multiprecision::et_off> cpp_dec_float_50_noet;

Here, we first define mp_backend as cpp_dec_float with 50 digits. The second step passes this backend to boost::multiprecision::number with boost::multiprecision::et_off, an enumerated type.

typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<50>, boost::multiprecision::et_off>
cpp_dec_float_50_noet;

You can reduce typing with a using directive using namespace boost::multiprecision; if desired, as shown below.

using namespace boost::multiprecision;

Now cpp_dec_float_50_noet or cpp_dec_float_50_et can be used as a direct replacement for built-in types like double etc.

BOOST_AUTO_TEST_CASE(cpp_float_test_check_close_noet)
{ // No expression templates/
  typedef number<cpp_dec_float<50>, et_off> cpp_dec_float_50_noet;

  std::cout.precision(std::numeric_limits<cpp_dec_float_50_noet>::digits10); // All significant digits.
  std::cout << std::showpoint << std::endl; // Show trailing zeros.

  cpp_dec_float_50_noet a ("1.0");
  cpp_dec_float_50_noet b ("1.0");
  b += std::numeric_limits<cpp_dec_float_50_noet>::epsilon(); // Increment least significant decimal digit.

  cpp_dec_float_50_noet eps = std::numeric_limits<cpp_dec_float_50_noet>::epsilon();

  std::cout <<"a = " << a << ",\nb = " << b << ",\neps = " << eps << std::endl;

  BOOST_CHECK_CLOSE(a, b, eps * 100); // Expected to pass (because tolerance is as percent).
  BOOST_CHECK_CLOSE_FRACTION(a, b, eps); // Expected to pass (because tolerance is as fraction).



} // BOOST_AUTO_TEST_CASE(cpp_float_test_check_close)

BOOST_AUTO_TEST_CASE(cpp_float_test_check_close_et)
{ // Using expression templates.
  typedef number<cpp_dec_float<50>, et_on> cpp_dec_float_50_et;

  std::cout.precision(std::numeric_limits<cpp_dec_float_50_et>::digits10); // All significant digits.
  std::cout << std::showpoint << std::endl; // Show trailing zeros.

  cpp_dec_float_50_et a("1.0");
  cpp_dec_float_50_et b("1.0");
  b += std::numeric_limits<cpp_dec_float_50_et>::epsilon(); // Increment least significant decimal digit.

  cpp_dec_float_50_et eps = std::numeric_limits<cpp_dec_float_50_et>::epsilon();

  std::cout << "a = " << a << ",\nb = " << b << ",\neps = " << eps << std::endl;

  BOOST_CHECK_CLOSE(a, b, eps * 100); // Expected to pass (because tolerance is as percent).
  BOOST_CHECK_CLOSE_FRACTION(a, b, eps); // Expected to pass (because tolerance is as fraction).

Using cpp_dec_float_50 with the default expression template use switched on, the compiler error message for `BOOST_CHECK_CLOSE_FRACTION(a, b, eps); would be:

// failure floating_point_comparison.hpp(59): error C2440: 'static_cast' :
// cannot convert from 'int' to 'boost::multiprecision::detail::expression<tag,Arg1,Arg2,Arg3,Arg4>'

A full example code is at test_cpp_float_close_fraction.cpp


PrevUpHomeNext