<#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;
}}
Тип<logged_adaptor
>используется в сочетании с<number
>и некоторым другим типом бэкэнда: он действует как тонкая обертка вокруг другого бэкэнда класса<number
>и регистрирует все события, которые происходят на этом объекте. Перед операцией любого числа он вызывает<log_prefix_event
>с аргументами к операции (до 4), плюс строку, описывающую операцию. Затем после операции он вызывает<log_postfix_event
>с результатом операции плюс строку, описывающую операцию. Необязательно,<log_postfix_event
>принимает второй аргумент результата: это происходит, когда результат операции не является<number
>, например, когда<fpclassify
>вызывается,<log_postfix_event
>будет вызываться с<result1
>будучи аргументом к функции, и<result2
>будучи целым результатом<fpclassify
>.
По умолчанию версии<log_prefix_event
>и<log_postfix_event
>ничего не делают, поэтому пользователь должен перегрузить их для конкретного наблюдаемого бэкэнда.
Этот тип обеспечивает<numeric_limits
>поддержку, когда аргумент шаблона Backend делает это.
Этот тип особенно полезен в сочетании с типом интервальных чисел — в этом случае можно использовать<log_postfix_event
>для мониторинга ошибки, накопленной после каждой операции. Мы могли бы либо установить какую-то ловушку, когда накопленная ошибка превышает какой-то порог, либо просто распечатать диагностическую информацию. Используя этот метод, мы можем быстро найти причину численной нестабильности в конкретной рутине. Следующий пример демонстрирует эту технику в тривиальном алгоритме, который намеренно вводит ошибку отмены:
#include <boost/multiprecision/mpfi.hpp>
#include <boost/multiprecision/logged_adaptor.hpp>
#include <iostream>
#include <iomanip>
namespace boost{ namespace multiprecision{
template <unsigned D>
inline void log_postfix_event(const mpfi_float_backend<D>& val, const char* event_description)
{
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)
{
}
}}
int main()
{
using namespace boost::multiprecision;
typedef number<logged_adaptor<mpfi_float_backend<17> > > logged_type;
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;
}
Когда мы исследуем выход программы, мы видим, что диаметр интервала увеличивается после каждого вычитания:
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}