使用Multiprecision增强间隔

时间:2017-11-25 16:44:18

标签: c++ boost intervals multiprecision boost-multiprecision

我尝试将boost间隔算术库与boost multiprecision库一起使用。如果我使用本机double数据类型的标准双精度,一切正常。

然而,使用multiprecision库,它会产生实际上更不准确的结果。这里有一些代码:

#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函数的最大值,所以这个边界完全没用。

如何解决这个问题,使得区间库实际上利用了更高的精度并产生更清晰的边界?

1 个答案:

答案 0 :(得分: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>()),它将它们表示为整数区间,例如:{{1}表示为[1,2]。 OOPS。 cos间隔变为pi_half<BigFloat>(递归到[-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>()

现在你可以写

<强> Live On Coliru

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

打印

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

如您所见,这不是所需的输出。

专业常量

实际上,你应该专门研究基础常量:

<强> Live On Coliru

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

打印:

#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] 常数