| Home | Libraries | People | FAQ | More

### 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