簡體   English   中英

像 union_ 這樣的 Boost.Geometry 操作如何處理浮點類型的基本不精確性?

[英]How do Boost.Geometry operations like union_ deal with the fundamental imprecision of floating point types?

我正在嘗試判斷是否/如何使 Boost.Geometry 適用於特定用例。 但是,我無法在任何地方找到有關該庫如何處理浮點類型的文檔。

如果您在官方文檔中搜索“epsilon”這個詞,據我所知,您的命中率為零; 但是,從庫的行為中可以清楚地看出,它在進行比較時隱式使用了處理浮點數的典型方式的某個版本,因為例如,union_ 操作將合並兩個彼此靠近但不重疊的多邊形,如果它們是夠近了。

例如,考慮以下代碼,該代碼執行二進制搜索以確定兩個單位正方形在合並時被認為是相鄰的閾值距離:

namespace bg = boost::geometry;

using point = bg::model::d2::point_xy<double>;
using polygon = bg::model::polygon<point, false>;

polygon create_poly(std::vector<std::tuple<double, double>> 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;
}

double find_epsilon(double left, double right) {

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

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

    polygon b = create_poly(
        std::vector<std::tuple<double, double>>{
            {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()
{
    auto eps = find_epsilon(0.0, 1.0);
    std::cout << "eps == " << eps << "\n";
}

當我使用 Visual Studio 編譯並運行上述內容時,我得到 output

每股收益 == 1e-07

這是關於單精度浮點數的數值限制 epsilon。 因此,如果雙精度坐標彼此在單精度 epsilon 范圍內,它是否將它們視為等價的?

基本上我只想知道默認行為是什么,以便我可以決定它是否適合我。

在 [介紹][1] 中,它指出:

該庫支持高精度算術數字,例如 ttmath。 [1]: https://www.boost.org/doc/libs/1_70_0/libs/geometry/doc/html/geometry/introduction.html

設計的基本原理更多地涉及到這一點:

[...],它會太長,並且與幾何無關。 我們只是假設有一個元函數 select_most_precise 選擇最佳類型。

他們還按照 OGC 簡單功能規范實現,這可能意味着您可以在那里找到更多算法穩健性保證

我從閱讀代碼中知道,某些算法會考慮邊緣情況,從而可以使結果更加健壯(通過按特定順序執行操作或注意特征何時非常接近,IIRC)。 一個簡單的 grep (例如robust的)可能會向您展示那里的一些進展:

policies/robustness/robust_point_type.hpp: // Meta-function to typedef a robust point type for a poli

algorithm/detail/overlay/get_turn_info_helpers.hpp: // Used ranges - owned by get_turns or (for

algorithm/detail/overlay/get_turn_info_helpers.hpp: // Version with rescaling, having robust points

algorithm/detail/overlay/append_no_dups_or_spikes.hpp: // Try using specified robust policy

我只是在這里吃草,我並沒有聲稱了解那里注意到的大部分內容。

使用任意精度或小數

精度是一個維度,輸入為十進制形式時的源保真度是另一個維度。 沒有去 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-49
Number: min_exp() -67108864
Number: max_exp() 67108864
Number: min()     1e-67108864
Number: max()     1e+67108864
eps == 0
zero? true

對於cpp_dec_float<0>它會打印(注意可變精度情況下的“奇怪” numeric_limits<>::eps`):

住在科利魯

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM