Skip to content

Instantly share code, notes, and snippets.

@eddieantonio
Created April 28, 2017 01:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddieantonio/ec9632c054cea7a574acf5c8c4c7789e to your computer and use it in GitHub Desktop.
Save eddieantonio/ec9632c054cea7a574acf5c8c4c7789e to your computer and use it in GitHub Desktop.
A C++ interval class with test cases.
/**
* Defining an interval on integer and floating point number lines.
*
* Compile with:
*
* $ c++ -std=c++11 interval.cc -o interval
*
* Run tests:
*
* $ ./interval
*
*/
#include <cassert>
#include <iostream>
#include <exception>
#include <cmath>
class BaseInterval {
public:
enum Location { Less = -1, Within = 0, Greater = 1 };
};
template <class T>
class Interval : public BaseInterval {
private:
const T _left, _right;
public:
Interval(T a, T b)
// Always set left as min and right as max to simplify assumptions.
: _left(std::min(a, b)), _right(std::max(a, b)) { }
/**
* Where is x relative to the interval?
*/
Location where(T x) const {
if (x > right()) {
return Greater;
} else if (x < left()) {
return Less;
} else if ((x >= left()) && (x <= right())) {
return Within;
}
// It's NaN or incomparable.
throw std::domain_error("Cannot determine location of NaN");
}
/*== Accessors ==*/
/**
* Leftmost bound.
*/
T left() const {
return _left;
}
/**
* Rightmost bound.
*/
T right() const {
return _right;
}
};
namespace {
template <class T>
void test_case() {
using Location = BaseInterval::Location;
// "ordinary" cases
{
Interval<T> a(-1, 1);
assert(a.where(2) == Location::Greater);
}
{
Interval<T> a(-1, 1);
assert(a.where(-2) == Location::Less);
}
{
Interval<T> a(-1, 1);
assert(a.where(0) == Location::Within);
}
// boundary cases
{
const auto lower = -1;
Interval<T> a(lower, 1);
assert(a.where(lower) == Location::Within);
}
{
const auto upper = 1;
Interval<T> a(-1, upper);
assert(a.where(upper) == Location::Within);
}
// degenerate cases
{
const auto point = 0;
Interval<T> a(point, point);
assert(a.where(point) == Location::Within);
}
{
const auto point = 0;
Interval<T> a(point, point);
assert(a.where(point - 1) == Location::Less);
}
{
const auto point = 0;
Interval<int> a(point, point);
assert(a.where(point + 1) == Location::Greater);
}
// "flipped" cases
{
Interval<T> a(1, -1);
assert(a.where(2) == Location::Greater);
}
{
Interval<T> a(1, -1);
assert(a.where(-2) == Location::Less);
}
{
Interval<T> a(1, -1);
assert(a.where(0) == Location::Within);
}
// flipped boundary cases
{
const auto lower = -1;
Interval<T> a(1, lower);
assert(a.where(lower) == Location::Within);
}
{
const auto upper = 1;
Interval<T> a(upper, -1);
assert(a.where(upper) == Location::Within);
}
};
template <class T>
void ieee754_test_cases() {
using Location = BaseInterval::Location;
// When bounds are infinite, we're always in the bounds.
{
Interval<T> a(-INFINITY, 0);
assert(a.where(-INFINITY) == Location::Within);
}
{
Interval<T> a(0, INFINITY);
assert(a.where(INFINITY) == Location::Within);
}
{
Interval<T> a(-INFINITY, INFINITY);
assert(a.where(0) == Location::Within);
}
{
Interval<T> a(INFINITY, -INFINITY);
assert(a.where(0) == Location::Within);
}
// Where is a NaN? Nowhere!
{
Interval<double> a(-INFINITY, INFINITY);
try {
a.where(NAN);
assert(false);
} catch (std::domain_error &e) {
assert(true);
}
}
}
}
int main(int argc, char *argv[]) {
test_case<char>();
test_case<short>();
test_case<int>();
test_case<long>();
test_case<float>();
test_case<double>();
test_case<long double>();
ieee754_test_cases<float>();
ieee754_test_cases<double>();
ieee754_test_cases<long double>();
std::cout << "👍\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment