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

logged_adaptor

#include <boost/multiprecision/logged_adaptor.hpp>

namespace boost{ namespace multiprecision{

template <class Backend>
void log_postfix_event(const Backend& result, const char* event_description);
template <class Backend, class T>
void log_postfix_event(const Backend& result1, const T& result2, const char* event_description);

template <class Backend>
void log_prefix_event(const Backend& arg1, const char* event_description);
template <class Backend, class T>
void log_prefix_event(const Backend& arg1, const T& arg2, const char* event_description);
template <class Backend, class T, class U>
void log_prefix_event(const Backend& arg1, const T& arg2, const U& arg3, const char* event_description);
template <class Backend, class T, class U, class V>
void log_prefix_event(const Backend& arg1, const T& arg2, const U& arg3, const V& arg4, const char* event_description);

template <Backend>
class logged_adaptor;

}} // namespaces

The logged_adaptor type is used in conjunction with number and some other backend type: it acts as a thin wrapper around some other backend to class number and logs all the events that take place on that object. Before any number operation takes place, it calls log_prefix_event with the arguments to the operation (up to 4), plus a string describing the operation. Then after the operation it calls log_postfix_event with the result of the operation, plus a string describing the operation. Optionally, log_postfix_event takes a second result argument: this occurs when the result of the operation is not a number, for example when fpclassify is called, log_postfix_event will be called with result1 being the argument to the function, and result2 being the integer result of fpclassify.

The default versions of log_prefix_event and log_postfix_event do nothing, it is therefore up to the user to overload these for the particular backend being observed.

This type provides numeric_limits support whenever the template argument Backend does so.

This type is particularly useful when combined with an interval number type - in this case we can use log_postfix_event to monitor the error accumulated after each operation. We could either set some kind of trap whenever the accumulated error exceeds some threshold, or simply print out diagnostic information. Using this technique we can quickly locate the cause of numerical instability in a particular routine. The following example demonstrates this technique in a trivial algorithm that deliberately introduces cancellation error:

#include <boost/multiprecision/mpfi.hpp>
#include <boost/multiprecision/logged_adaptor.hpp>
#include <iostream>
#include <iomanip>
//
// Begin by overloading log_postfix_event so we can capture each arithmetic event as it happens:
//
namespace boost{ namespace multiprecision{

template <unsigned D>
inline void log_postfix_event(const mpfi_float_backend<D>& val, const char* event_description)
{
   // Print out the (relative) diameter of the interval:
   using namespace boost::multiprecision;
   number<mpfr_float_backend<D> > diam;
   mpfi_diam(diam.backend().data(), val.data());
   std::cout << "Diameter was " << diam << " after operation: " << event_description << std::endl;
}
template <unsigned D, class T>
inline void log_postfix_event(const mpfi_float_backend<D>&, const T&, const char* event_description)
{
   // This version is never called in this example.
}

}}


int main()
{
   using namespace boost::multiprecision;
   typedef number<logged_adaptor<mpfi_float_backend<17> > > logged_type;
   //
   // Test case deliberately introduces cancellation error, relative size of interval
   // gradually gets larger after each operation:
   //
   logged_type a = 1;
   a /= 10;

   for(unsigned i = 0; i < 13; ++i)
   {
      logged_type b = a * 9;
      b /= 10;
      a -= b;
   }
   std::cout << "Final value was: " << a << std::endl;
   return 0;
}

When we examine program output we can clearly see that the diameter of the interval increases after each subtraction:

Diameter was nan after operation: Default construct
Diameter was 0 after operation: Assignment from arithmetic type
Diameter was 4.33681e-18 after operation: /=
Diameter was nan after operation: Default construct
Diameter was 7.70988e-18 after operation: *
Diameter was 9.63735e-18 after operation: /=
Diameter was 1.30104e-16 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 1.30104e-16 after operation: *
Diameter was 1.38537e-16 after operation: /=
Diameter was 2.54788e-15 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 2.54788e-15 after operation: *
Diameter was 2.54863e-15 after operation: /=
Diameter was 4.84164e-14 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 4.84164e-14 after operation: *
Diameter was 4.84221e-14 after operation: /=
Diameter was 9.19962e-13 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 9.19962e-13 after operation: *
Diameter was 9.19966e-13 after operation: /=
Diameter was 1.74793e-11 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 1.74793e-11 after operation: *
Diameter was 1.74793e-11 after operation: /=
Diameter was 3.32107e-10 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 3.32107e-10 after operation: *
Diameter was 3.32107e-10 after operation: /=
Diameter was 6.31003e-09 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 6.31003e-09 after operation: *
Diameter was 6.31003e-09 after operation: /=
Diameter was 1.19891e-07 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 1.19891e-07 after operation: *
Diameter was 1.19891e-07 after operation: /=
Diameter was 2.27792e-06 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 2.27792e-06 after operation: *
Diameter was 2.27792e-06 after operation: /=
Diameter was 4.32805e-05 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 4.32805e-05 after operation: *
Diameter was 4.32805e-05 after operation: /=
Diameter was 0.00082233 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 0.00082233 after operation: *
Diameter was 0.00082233 after operation: /=
Diameter was 0.0156243 after operation: -=
Diameter was nan after operation: Default construct
Diameter was 0.0156243 after operation: *
Diameter was 0.0156243 after operation: /=
Diameter was 0.296861 after operation: -=
Final value was: {8.51569e-15,1.14843e-14}


PrevUpHomeNext