Skip to content

Commit b018bc7

Browse files
author
Mikael Zayenz Lagerkvist
committed
Preserve float median semantics safely
1 parent 18ed77a commit b018bc7

2 files changed

Lines changed: 42 additions & 16 deletions

File tree

gecode/float/val.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,22 @@ namespace Gecode {
8282
FloatVal::med(void) const {
8383
const FloatNum l = x.lower();
8484
const FloatNum u = x.upper();
85-
if ((l < 0.0) && (u > 0.0))
86-
return l / 2.0 + u / 2.0;
87-
return l + (u - l) / 2.0;
85+
const FloatNum inf = std::numeric_limits<FloatNum>::infinity();
86+
const FloatNum max = std::numeric_limits<FloatNum>::max();
87+
if (l == -inf) {
88+
if (u == -inf)
89+
return l;
90+
// Preserve the extended endpoint for semi-infinite intervals.
91+
return (u == inf) ? 0.0 : l;
92+
}
93+
if (u == inf)
94+
return u;
95+
96+
Float::Rounding r;
97+
const FloatNum h = max / 2.0;
98+
if ((u > h) || (l < -h))
99+
return std::ldexp(r.median(l / 2.0,u / 2.0),1);
100+
return r.median(l,u);
88101
}
89102

90103
forceinline bool

test/float/basic.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,32 +76,45 @@ namespace Test { namespace Float {
7676
Basic b2(Gecode::FloatVal(-2,10),1.5);
7777
}
7878

79-
/// Test that interval medians remain finite at the numerical limits
80-
class MedianLimits : public Base {
79+
/// Test interval medians at the numerical limits and special values
80+
class Median : public Base {
81+
private:
82+
/// Test median inclusion and splitting of non-tight intervals
83+
static bool valid(const Gecode::FloatVal& x) {
84+
const Gecode::FloatNum m = x.med();
85+
return (m >= x.min()) && (m <= x.max()) &&
86+
(x.tight() || ((m > x.min()) && (m < x.max())));
87+
}
8188
public:
8289
/// Create and register test
83-
MedianLimits(void) : Base("Float::Basic::MedianLimits") {}
90+
Median(void) : Base("Float::Basic::Median") {}
8491
/// Perform test
8592
virtual bool run(void) {
8693
using namespace Gecode;
8794
const FloatNum m = Gecode::Float::Limits::max;
95+
const FloatNum i = std::numeric_limits<FloatNum>::infinity();
96+
const FloatNum d = std::numeric_limits<FloatNum>::denorm_min();
8897
const FloatVal negative(-m, -m / 2.0);
8998
const FloatVal positive(m / 2.0, m);
9099
const FloatVal mixed(-m, m);
91-
return std::isfinite(negative.med()) &&
92-
(negative.med() >= negative.min()) &&
93-
(negative.med() <= negative.max()) &&
94-
std::isfinite(positive.med()) &&
95-
(positive.med() >= positive.min()) &&
96-
(positive.med() <= positive.max()) &&
97-
std::isfinite(mixed.med()) &&
98-
(mixed.med() >= mixed.min()) &&
99-
(mixed.med() <= mixed.max());
100+
// The subtraction-based formula rounds this median one ULP upward.
101+
const FloatVal closest(3.07187504409887e-53,
102+
7.805365587017815e-45);
103+
return valid(negative) && valid(positive) && valid(mixed) &&
104+
(closest.med() == 3.902682808868283e-45) &&
105+
(FloatVal(-i,-1.0).med() == -i) &&
106+
(FloatVal(1.0,i).med() == i) &&
107+
(FloatVal(-i,i).med() == 0.0) &&
108+
(FloatVal(-i,-i).med() == -i) &&
109+
(FloatVal(i,i).med() == i) &&
110+
!std::signbit(FloatVal(-0.0,0.0).med()) &&
111+
(FloatVal(0.0,2.0*d).med() == d) &&
112+
(FloatVal(-2.0*d,0.0).med() == -d);
100113
}
101114
};
102115

103116
namespace {
104-
MedianLimits ml;
117+
Median median;
105118
}
106119
//@}
107120

0 commit comments

Comments
 (0)