[英]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.