C++ Boost.Geometry操作(如union)如何处理浮点类型的基本不精确性?

C++ Boost.Geometry操作(如union)如何处理浮点类型的基本不精确性?,c++,boost-geometry,C++,Boost Geometry,我正在尝试判断是否/如何使Boost.Geometry适用于特定用例。但是,我在任何地方都找不到关于该库如何处理浮点类型的文档 如果你在官方文档中搜索“epsilon”一词,我能告诉你的是,你没有得到任何点击;但是,从库的行为可以清楚地看出,它隐式地使用了在进行比较时处理浮动的典型方法的某些版本,因为,例如,union_u操作将合并两个彼此相邻但如果足够近则不重叠的多边形 例如,考虑以下代码,该代码执行二进制搜索,以确定并集时两个单位正方形需要在其范围内才能视为相邻的阈值距离: namespac

我正在尝试判断是否/如何使Boost.Geometry适用于特定用例。但是,我在任何地方都找不到关于该库如何处理浮点类型的文档

如果你在官方文档中搜索“epsilon”一词,我能告诉你的是,你没有得到任何点击;但是,从库的行为可以清楚地看出,它隐式地使用了在进行比较时处理浮动的典型方法的某些版本,因为,例如,union_u操作将合并两个彼此相邻但如果足够近则不重叠的多边形

例如,考虑以下代码,该代码执行二进制搜索,以确定并集时两个单位正方形需要在其范围内才能视为相邻的阈值距离:

namespace bg=boost::geometry;
使用point=bg::model::d2::point_xy;
使用polygon=bg::model::polygon;
多边形创建_多边形(标准::向量pts){
多边形多边形;
用于(常数自动和[x,y]:pts)
追加(poly,bg::make(x,y));
自动[x_1,y_1]=pts[0];
追加(poly,bg::make(x_1,y_1));
返回多边形;
}
布尔执行简单并集(常量多边形和p1、常量多边形和p2){
std::矢量输出;
bg::并集(p1,p2,输出);
返回输出。size()==1;
}
双查找ε(双左,双右){
if(右-左std::cout在[简介][1]中,它指出:

该库支持高精度算术数字,如ttmath。 [1] :

图书馆对此进行了更深入的探讨:

[…],它太长了,而且与几何无关。我们只是假设有一个元函数select_most_精确地选择最佳类型

它们还按照OGC简单特性规范实现,这可能意味着您可以找到

通过阅读代码,我知道有一些算法考虑了边缘情况,在这些情况下,结果可以变得更加稳健(通过按特定顺序进行操作或注意功能非常接近,IIRC)。例如,
robust
的简单grep可能会向您展示一些道路:

policies/robustness/robust_point_type.hpp:
//用于定义poli的robust point type的元函数

算法/detail/overlay/get\u turn\u info\u helpers.hpp:
//使用范围-归get\u turns或(for

算法/detail/overlay/get\u turn\u info\u helpers.hpp:
//具有重缩放功能的版本,具有健壮的点

算法/detail/overlay/append\u no\u dups\u或\u spikes.hpp:
//尝试使用指定的健壮策略

我只是粗略地看了一下这里的情况,我并没有说我了解那里的很多情况

使用任意精度或小数 精度是一个维度,输入为十进制形式时的源保真度是另一个维度。除了转到MPFR/GMP/ttmath(如前所述),您可以轻松进入Boost Multiprecision。这为您提供了快速的概念证明,因为它随Boost一起提供,还允许您透明地切换到GMP或MPFR后端

另见:

#include <boost/geometry.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
namespace mp = boost::multiprecision;
namespace bg = boost::geometry;

//// Note, cpp_dec_float<0> is variable-precision!
// using Number = mp::number<mp::cpp_dec_float<0>, mp::et_off>;

// Fixed precision, avoids allocating and populates std::numeric_limits<>
// with concrete data
using Number = mp::number<mp::cpp_dec_float<50>, mp::et_off>;

using point = boost::geometry::model::d2::point_xy<Number>;
using polygon = bg::model::polygon<point, false>;

polygon create_poly(std::vector<std::tuple<Number, Number>> pts) {
    polygon poly;
    for (const auto& [x, y] : pts)
        bg::append(poly, bg::make<point>(x, y));
    auto [x_1, y_1] = pts[0];
    bg::append(poly, bg::make<point>(x_1, y_1));
    return poly;
}

bool perform_simple_union(const polygon& p1, const polygon& p2) {
    std::vector<polygon> output; 
    bg::union_(p1, p2, output);
    return output.size() == 1;
}

Number find_epsilon(Number left, Number right) {

    Number eps = (left + right) / 2;
    if (right - left < std::numeric_limits<Number>::epsilon())
        return left;

    polygon a = create_poly(
        std::vector<std::tuple<Number, Number>>{
            {1.0, 1.0}, { 2.0,1.0 }, { 2.0, 2.0 }, { 1.0,2.0 }
        }
    );

    polygon b = create_poly(
        std::vector<std::tuple<Number, Number>>{
            {2.0 + eps, 1.0}, { 3.0 + eps, 1.0 }, { 3.0 + eps, 2.0 }, { 2.0 + eps,2.0 }
        }
    );

    if ( perform_simple_union(a, b) ) {
        return find_epsilon(eps, right);
    } else {
        return find_epsilon(left, eps);
    }
}

int main()
{
    std::cout << "nextafter(0, 1):  " << nextafter(Number(0), Number(1)) << "\n";
    std::cout << "Number: eps()     " << std::numeric_limits<Number>::epsilon()      << "\n";
    std::cout << "Number: min_exp() " << std::numeric_limits<Number>::min_exponent10 << "\n";
    std::cout << "Number: max_exp() " << std::numeric_limits<Number>::max_exponent10 << "\n";
    std::cout << "Number: min()     " << std::numeric_limits<Number>::min()          << "\n";
    std::cout << "Number: max()     " << std::numeric_limits<Number>::max()          << "\n";

    auto eps = find_epsilon(0.0, 1.0);

    std::cout << std::setprecision(180);
    std::cout << "eps == " << eps << "\n";

    std::cout << std::boolalpha;
    std::cout << "zero? " << (eps == 0) << "\n";
}
nextafter(0, 1):  1e-67108864
Number: eps()     1e-08
Number: min_exp() -67108864
Number: max_exp() 67108864
Number: min()     1e-67108864
Number: max()     1e+67108864
eps == 0
zero? true
对于
cpp\u dec\u float
它会打印(注意在可变精度情况下的“奇怪”数值限制::eps`):

#include <boost/geometry.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
namespace mp = boost::multiprecision;
namespace bg = boost::geometry;

//// Note, cpp_dec_float<0> is variable-precision!
// using Number = mp::number<mp::cpp_dec_float<0>, mp::et_off>;

// Fixed precision, avoids allocating and populates std::numeric_limits<>
// with concrete data
using Number = mp::number<mp::cpp_dec_float<50>, mp::et_off>;

using point = boost::geometry::model::d2::point_xy<Number>;
using polygon = bg::model::polygon<point, false>;

polygon create_poly(std::vector<std::tuple<Number, Number>> pts) {
    polygon poly;
    for (const auto& [x, y] : pts)
        bg::append(poly, bg::make<point>(x, y));
    auto [x_1, y_1] = pts[0];
    bg::append(poly, bg::make<point>(x_1, y_1));
    return poly;
}

bool perform_simple_union(const polygon& p1, const polygon& p2) {
    std::vector<polygon> output; 
    bg::union_(p1, p2, output);
    return output.size() == 1;
}

Number find_epsilon(Number left, Number right) {

    Number eps = (left + right) / 2;
    if (right - left < std::numeric_limits<Number>::epsilon())
        return left;

    polygon a = create_poly(
        std::vector<std::tuple<Number, Number>>{
            {1.0, 1.0}, { 2.0,1.0 }, { 2.0, 2.0 }, { 1.0,2.0 }
        }
    );

    polygon b = create_poly(
        std::vector<std::tuple<Number, Number>>{
            {2.0 + eps, 1.0}, { 3.0 + eps, 1.0 }, { 3.0 + eps, 2.0 }, { 2.0 + eps,2.0 }
        }
    );

    if ( perform_simple_union(a, b) ) {
        return find_epsilon(eps, right);
    } else {
        return find_epsilon(left, eps);
    }
}

int main()
{
    std::cout << "nextafter(0, 1):  " << nextafter(Number(0), Number(1)) << "\n";
    std::cout << "Number: eps()     " << std::numeric_limits<Number>::epsilon()      << "\n";
    std::cout << "Number: min_exp() " << std::numeric_limits<Number>::min_exponent10 << "\n";
    std::cout << "Number: max_exp() " << std::numeric_limits<Number>::max_exponent10 << "\n";
    std::cout << "Number: min()     " << std::numeric_limits<Number>::min()          << "\n";
    std::cout << "Number: max()     " << std::numeric_limits<Number>::max()          << "\n";

    auto eps = find_epsilon(0.0, 1.0);

    std::cout << std::setprecision(180);
    std::cout << "eps == " << eps << "\n";

    std::cout << std::boolalpha;
    std::cout << "zero? " << (eps == 0) << "\n";
}
nextafter(0, 1):  1e-67108864
Number: eps()     1e-08
Number: min_exp() -67108864
Number: max_exp() 67108864
Number: min()     1e-67108864
Number: max()     1e+67108864
eps == 0
zero? true

这很有趣。因此,双精度案例似乎使用基于epsilon的相等定义这一事实并不是故意的,而是双精度的产物,只是不够精确?实际上,我已经计划在我正在开发的应用程序中使用boost::multiprecision数字,只是还没有决定使用哪种粒子lar one。我认为它仍然是基于ε的。只是有一个更远的“视界”。它不是明确的ε“定向”(他们已经记录了这一点),但在实践中,围绕ε值的比较会变得不准确。只是一个实验来说明“ε”如何随量级变化: