diff --git a/include/boost/geometry/index/detail/algorithms/content.hpp b/include/boost/geometry/index/detail/algorithms/content.hpp index 1ac56fccb1..2ea13dd00a 100644 --- a/include/boost/geometry/index/detail/algorithms/content.hpp +++ b/include/boost/geometry/index/detail/algorithms/content.hpp @@ -112,6 +112,19 @@ typename default_content_result::type content(Indexable const& b) >::apply(b); } +// Returns the content increase when expanding 'original' to 'expanded'. +// Precondition: 'original' is covered by 'expanded'. Under this precondition +// the result is mathematically non-negative, so clamping to 0 is valid and +// guards against floating-point contraction artifacts (e.g. GCC FMA fusion +// across function boundaries) that can make the difference negative or +// spuriously non-zero for equal boxes. See GitHub issue #1452. +template +typename default_content_result::type content_diff(Box const& expanded, Box const& original) +{ + using content_type = typename default_content_result::type; + return (std::max)(content_type(0), content(expanded) - content(original)); +} + #if defined(BOOST_GCC) #pragma GCC pop_options #endif diff --git a/include/boost/geometry/index/detail/rtree/linear/redistribute_elements.hpp b/include/boost/geometry/index/detail/rtree/linear/redistribute_elements.hpp index 4cba83b6be..7c4dcc0f78 100644 --- a/include/boost/geometry/index/detail/rtree/linear/redistribute_elements.hpp +++ b/include/boost/geometry/index/detail/rtree/linear/redistribute_elements.hpp @@ -16,6 +16,7 @@ #ifndef BOOST_GEOMETRY_INDEX_DETAIL_RTREE_LINEAR_REDISTRIBUTE_ELEMENTS_HPP #define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_LINEAR_REDISTRIBUTE_ELEMENTS_HPP +#include #include #include @@ -432,8 +433,10 @@ struct redistribute_elements content_type enlarged_content1 = index::detail::content(enlarged_box1); content_type enlarged_content2 = index::detail::content(enlarged_box2); - content_type content_increase1 = enlarged_content1 - content1; - content_type content_increase2 = enlarged_content2 - content2; + content_type const content_increase1 + = (std::max)(content_type(0), enlarged_content1 - content1); + content_type const content_increase2 + = (std::max)(content_type(0), enlarged_content2 - content2); // choose group which box content have to be enlarged least or has smaller content or has fewer elements if ( content_increase1 < content_increase2 || diff --git a/include/boost/geometry/index/detail/rtree/quadratic/redistribute_elements.hpp b/include/boost/geometry/index/detail/rtree/quadratic/redistribute_elements.hpp index 31ee5cc842..dd06427420 100644 --- a/include/boost/geometry/index/detail/rtree/quadratic/redistribute_elements.hpp +++ b/include/boost/geometry/index/detail/rtree/quadratic/redistribute_elements.hpp @@ -74,9 +74,10 @@ inline void pick_seeds(Elements const& elements, bounded_indexable_view bounded_ind1(ind1, strategy); bounded_indexable_view bounded_ind2(ind2, strategy); - content_type free_content = ( index::detail::content(enlarged_box) - - index::detail::content(bounded_ind1) ) - - index::detail::content(bounded_ind2); + content_type free_content = (std::max)(content_type(0), + ( index::detail::content(enlarged_box) + - index::detail::content(bounded_ind1) ) + - index::detail::content(bounded_ind2)); if ( greatest_free_content < free_content ) { @@ -273,7 +274,7 @@ struct redistribute_elements typedef typename boost::iterator_value::type element_type; typedef typename rtree::element_indexable_type::type indexable_type; - content_type greatest_content_incrase_diff = 0; + content_type greatest_content_increase_diff = 0; It out_it = first; out_content_increase1 = 0; out_content_increase2 = 0; @@ -291,18 +292,20 @@ struct redistribute_elements content_type enlarged_content1 = index::detail::content(enlarged_box1); content_type enlarged_content2 = index::detail::content(enlarged_box2); - content_type content_incrase1 = (enlarged_content1 - content1); - content_type content_incrase2 = (enlarged_content2 - content2); + content_type const content_increase1 + = (std::max)(content_type(0), enlarged_content1 - content1); + content_type const content_increase2 + = (std::max)(content_type(0), enlarged_content2 - content2); - content_type content_incrase_diff = content_incrase1 < content_incrase2 ? - content_incrase2 - content_incrase1 : content_incrase1 - content_incrase2; + content_type content_increase_diff = content_increase1 < content_increase2 ? + content_increase2 - content_increase1 : content_increase1 - content_increase2; - if ( greatest_content_incrase_diff < content_incrase_diff ) + if ( greatest_content_increase_diff < content_increase_diff ) { - greatest_content_incrase_diff = content_incrase_diff; + greatest_content_increase_diff = content_increase_diff; out_it = el_it; - out_content_increase1 = content_incrase1; - out_content_increase2 = content_incrase2; + out_content_increase1 = content_increase1; + out_content_increase2 = content_increase2; } } diff --git a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp index e673086893..251239ab7c 100644 --- a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp +++ b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp @@ -117,7 +117,7 @@ class choose_next_node // areas difference content_type content = index::detail::content(box_exp); - content_type content_diff = content - index::detail::content(ch_i.first); + content_type content_diff = index::detail::content_diff(box_exp, ch_i.first); children_contents[i].set(i, content, content_diff); @@ -246,7 +246,7 @@ class choose_next_node // areas difference content_type content = index::detail::content(box_exp); - content_type content_diff = content - index::detail::content(ch_i.first); + content_type content_diff = index::detail::content_diff(box_exp, ch_i.first); // update the result if ( content_diff < smallest_content_diff || diff --git a/include/boost/geometry/index/detail/rtree/visitors/insert.hpp b/include/boost/geometry/index/detail/rtree/visitors/insert.hpp index 1c046b1bde..e8229b136b 100644 --- a/include/boost/geometry/index/detail/rtree/visitors/insert.hpp +++ b/include/boost/geometry/index/detail/rtree/visitors/insert.hpp @@ -89,7 +89,7 @@ class choose_next_node // areas difference content_type content = index::detail::content(box_exp); - content_type content_diff = content - index::detail::content(ch_i.first); + content_type content_diff = index::detail::content_diff(box_exp, ch_i.first); // update the result if ( content_diff < smallest_content_diff ||