#6095 closed Bugs (fixed)
boost::icl::is_empty fails for certain open intervals
Reported by: | Owned by: | Joachim Faulhaber | |
---|---|---|---|
Milestone: | Boost 1.48.0 | Component: | ICL |
Version: | Boost 1.47.0 | Severity: | Problem |
Keywords: | icl, open interval, integer overflow | Cc: |
Description
While playing around with intervals over int
with INT_MAX
and INT_MIN
bounds, I encountered some assertion failures in the library. After a little debugging the problem boiled down to the following test:
BOOST_AUTO_TEST_CASE(isEmptyTest) { typedef int Value; typedef boost::icl::interval<Value> Interval; typedef std::numeric_limits<Value> Limits; Value const max(Limits::max()); BOOST_CHECK(!is_empty(Interval::open(max - 2, max))); BOOST_CHECK( is_empty(Interval::open(max - 1, max))); BOOST_CHECK( is_empty(Interval::open(max, max))); }
The last check fails due to an integer overflow in the implementation of is_empty
where the lower bound gets incremented.
I was able to fix this problem for is_empty
but there are many places in the ICL where domain_next
or domain_prior
is used and there might be more problems lurking there.
My motivation for these experiments was to get a properly total interval map, i.e. one where the iterators start at INT_MIN
and end at INT_MAX
. To get this, I 'primed' the empty map with a closed interval ranging over the complete domain value range. But then the problems started.
So at least for me these overflow problems are not just of academical interest.
Attachments (2)
Change History (10)
Changed 6 years ago by
Attachment: | interval.hpp.diff added |
---|
comment:1 Changed 6 years ago by
Hi Marvin,
thank you for your report and the attached patch. I agree that the behavior of icl::intervals
at open borders and for limited domain types is kind of nasty. Nevertheless after examining your information I am not convinced that we can speak of a bug here and that a code change is needed.
In your test case you propose that
BOOST_CHECK( is_empty(Interval::open(max, max)));
is supposed to succeed. But an open interval Interval::open(max, x)
has an open lower bound of max
, which means that values of its domain_type do not exist that can be elements of the interval. My interpretation of intervals of this kind is that they are ill formed rather than empty. So the user has to take care via program logic that these types of intervals are not constructed in the same way she has to take care that ++i
is not called if i == numeric_limits<int>::max()
already.
Best regards, Joachim
comment:2 Changed 6 years ago by
Hi Joachim,
as a mathematician I have to disagree: what distinguishes the interval Interval::open(max, max)
from Interval::open(0, 0)
? For both there exist no elements, that is the point of being empty. Even intervals like Interval::closed(17, 4)
are allowed and deemed empty!
As a programmer I might be able to accept the responsibility to avoid such cases, but only if I were able to do so. I encountered the problem not while putting open intervals into the container but closed ones. And for those borders like min
and max
certainly make sense.
What I was trying to do was
BOOST_AUTO_TEST_CASE(totalRangeTest) { typedef int Value; typedef boost::icl::interval<Value> Interval; typedef std::numeric_limits<Value> Limits; typedef boost::icl::interval_map<Value, int, boost::icl::total_enricher> Container; Value const min(Limits::min()); Value const max(Limits::max()); boost::icl::interval_map<Value, int, boost::icl::total_enricher> intervals; intervals += std::make_pair(Interval::closed(min, max), 0); intervals += std::make_pair(Interval::right_open(0, 10), 3); BOOST_CHECK_EQUAL(intervals.iterative_size(), 3); }
and this fails BOOST_ASSERT(this->_map.find(inter_val) == this->_map.end());
in interval_base_map.hpp
, line 530 while trying to calculate the new third element of the container (Interval::closed(10, max)
) when inserting the second interval.
Drilling down on that, I found is_empty
and its problematic behaviour.
Thank you for your response!
Best regards,
Marvin
comment:3 Changed 6 years ago by
Keywords: | icl open interval integer overflow added |
---|---|
Milestone: | To Be Determined → Boost 1.48.0 |
Status: | new → assigned |
Hi Marvin,
I hope you forgive me my temporal {arrog|ignor}ance ;) Your use case shows, that the user can hardly avoid hitting the overflow trap of open intervals that are computed internally in the library in your test case. Also your fix is a nifty one. So thanks again for the report. I will apply your fix and put you on the credits list :) Send in more use cases if you find more bugs related to the [min,max]
-preinitialized usage.
Cheers,
Joachim
comment:4 Changed 6 years ago by
Hi Joachim,
no problem :)
But I would like to propose a maybe even more nifty fix:
An open interval ]a, b[
is empty if b <= a || b <= a + 1
.
This does not only avoid the second increment, it even avoids the first one in many cases due to the short circuit ||
-operator and does never run into the overflow case:
If the first check fails, we know that a < b
and hence that there is at least one element to increment to without overflowing.
So this should even work for bound types that are less forgiving than int
when hitting their bounds, (e.g. throw exceptions).
Best regards,
Marvin
comment:5 Changed 6 years ago by
Hi Marvin,
yes, this is nice and simple. I changed the code accordingly.
Thanx again,
Joachim
comment:6 Changed 6 years ago by
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:7 Changed 6 years ago by
Component: | ICL → Building Boost |
---|---|
Type: | Bugs → Patches |
comment:8 Changed 6 years ago by
Component: | Building Boost → ICL |
---|---|
Type: | Patches → Bugs |
patch to check for overflow in is_empty