Повышение интервала с множественной точностью

Я пытаюсь использовать арифметическую библиотеку интервалов повышения вместе с библиотекой повышения точности. Если я использую стандартную двойную точность с собственным типом данных double, все работает нормально.

Однако с библиотекой многоточности она дает результаты, которые на самом деле более неточны. Вот код:

#include <boost\numeric\interval.hpp>
#include <boost\multiprecision\cpp_dec_float.hpp>
#include <iostream>

using namespace boost::numeric;
using namespace boost::numeric::interval_lib;
using namespace boost::multiprecision;

template <typename T>
using Interval = interval<T, policies<save_state<rounded_transc_exact<T>>, checking_base<T>>>;
using BigFloat = cpp_dec_float_100;

int main()
{
    std::cout << sin(Interval<double>(0.0, 0.1)).upper() << "\n"; // 0.0998334
    std::cout << sin(Interval<BigFloat>(0.0, 0.1)).upper() << "\n"; // 1
}

Как видно, версия double дает очень точный результат. Версия BigFloat должна быть еще более точной, однако она дает очень большую границу — фактически максимальное значение функции sin, так что эта граница совершенно бесполезна.

Как я могу это исправить, чтобы библиотека интервалов действительно использовала преимущества более высокой точности и давала более четкие границы?


person SampleTime    schedule 25.11.2017    source источник


Ответы (1)


Для начала я протестировал cos вместо sin.

Библиотека интервалов реализует sin(x) как cos(x-½π). Это означает, что sin([0, 0.1]) преобразуется в cos([-½π,-½π+0.1]) (которое рекурсивно превращается в cos([½π,½π+0.1])).

В случае BigFloat и из-за того, что библиотека не знает константы Pi (pi<BigFloat>(), pi_half<BigFloat>() или pi_twice<BigFloat>()), она представляет их как целые интервалы, например: pi_half<BigFloat> представляется как [1,2]. Ой. интервалы cos стали [-2,-0.9] (рекурсивно превращаясь в [0,3.1]¹).

Добавление трассировки:

DOUBLE--------------------
pi/2: [1.570796326794896558,1.57079632679489678]
sin: [0,0.10000000000000000555]
cos: [-1.57079632679489678,-1.4707963267948964692]
cos: [1.5707963267948961139,1.6707963267948979791]
[-5.0532154980743028885e-16,0.099833416646829500896]
BigFloat--------------------
pi/2: [1,2]
sin: [0,0.10000000000000000555]
cos: [-2,-0.89999999999999999445]
cos: [0,3.1000000000000000056]
[-1,1]

Решения?

Лучшее решение, которое я могу придумать, включает использование cos напрямую или специализацию pi_half:

Используйте cos напрямую

Это НЕ решение, потому что оно по-прежнему будет использовать некоторые сломанные константы pi_*<BigFloat>() внутри:

static BigFloat bf_pi_half() { return bmp::default_ops::get_constant_pi<BigFloat::backend_type>() / BigFloat(2); }

Теперь вы можете написать

Прямой эфир на Coliru

std::cout << "BigFloat--------------------\n";
std::cout << cos(ival - bf_pi_half()) << "\n";

Печать

BigFloat--------------------
[-0.909297,0.818277]

Как видите, это не тот результат, который вам нужен.

Специализированные константы

Фактически, вы должны специализировать базовые константы:

Жить на Coliru

#include <iostream>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/detail/default_ops.hpp>
#include <boost/numeric/interval.hpp>
#include <boost/numeric/interval/io.hpp>

namespace bn  = boost::numeric;
namespace bni = bn::interval_lib;
namespace bmp = boost::multiprecision;

template <typename T>
using Interval = bn::interval<T, bni::policies<bni::save_state<bni::rounded_transc_exact<T>>, bni::checking_base<T>>>;
using BigFloat = bmp::cpp_dec_float_100; // bmp::number<bmp::backends::cpp_dec_float<100>, bmp::et_off>;

static BigFloat bf_pi() { return bmp::default_ops::get_constant_pi<BigFloat::backend_type>(); }

namespace boost { namespace numeric { namespace interval_lib { namespace constants {
    template<> inline BigFloat pi_lower<BigFloat>()       { return bf_pi(); }
    template<> inline BigFloat pi_upper<BigFloat>()       { return bf_pi(); }
    template<> inline BigFloat pi_twice_lower<BigFloat>() { return bf_pi() * 2; }
    template<> inline BigFloat pi_twice_upper<BigFloat>() { return bf_pi() * 2; }
    template<> inline BigFloat pi_half_lower<BigFloat>()  { return bf_pi() / 2; }
    template<> inline BigFloat pi_half_upper<BigFloat>()  { return bf_pi() / 2; }
} } } }

int main() {
    std::cout << sin(Interval<BigFloat>(0, 0.1)) << "\n";
}

Это печатает:

[0,0.0998334]


¹ на самом деле используется константа pi_twice<>

person sehe    schedule 26.11.2017
comment
Исправлено решение для специализации всех необходимых констант. К сожалению, дизайн библиотеки не допускает частичных специализаций, поэтому мы не можем добавить специализации для всех типов boost::multiprecision::number<...>. Это печальное положение дел, и разработчики библиотеки действительно должны заняться этим. - person sehe; 26.11.2017
comment
Отличный ответ, это многое объясняет. Наконец-то заработало, спасибо. - person SampleTime; 26.11.2017