Consider intervals A = [x1, y1] and B = [x2, y2], two intervals representing signed integers.
Interval arithmetic page on Wikipedia gives a formula to address the case where B does not contain 0, whose C++ implementation may be as follows:
void print(const char* const what, int x, int y)
{
std::cout << what << " = [" << x << ", " << y << "]\n";
}
void div(const char* const what, int x1, int y1, int x2, int y2)
{
int v1 = x1/x2;
int v2 = x1/y2;
int v3 = y1/x2;
int v4 = y1/y2;
int x = std::min(std::min(v1, v2), std::min(v3, v4));
int y = std::max(std::max(v1, v2), std::max(v3, v4));
print(what, x, y);
}
int main()
{
int x1 = -10000, y1 = 150000;
int x2 = -10, y2 = 10;
print("A", x1, y1);
print("B", x2, y2);
div("A/B", x1, y1, x2, y2);
}
Output:
A = [-10000, 150000]
B = [-10, 10]
A/B = [-15000, 15000]
As expected, the result is incorrect given that B contains 0. For example, since 1 is part of B, 150000 should be within A/B but it is not.
What is a viable algorithm when 0 is excluded from B? Should the solution use multiple intervals around -1 and 1 (i.e. exclude 0) and join them somehow?
Edit: the solution may be expressed in terms of a union (vector) of intervals of long long type.
You are not writing C++, you are writing C wrapped in some little C++ then and there. So here's what I would do:
First I would start with a class for an interval, i.e.:
Then I would start with addition, subtraction and multiplication. E.g.
Which are quite simple.
When we get to division, as seen on your wiki link, things get more complicated.
∞and-∞heads.The first problem is solved with a new class:
For the second problem ... well we can no longer use
intas data. So you need a new class for integer that can contain the values∞, -∞, NaN.NaNis required as a byproduct, e.g.-∞ + ∞ = NaN.for which you would have to define 3 constants that are the new 3 values. You would then need to define all of the basic arithmetic operations on them.
Finally we get to redo everything to something like this:
As you can see things get complicated pretty fast.
As for how you would implement ExtendedInt one solution is to have two data members, one
intand one enumif
ext_value_ == Normalthen the value of the instance isvalue_, else it isext_value_.Another solution is to realize that functionally that is an union. So you could use
std::variant<int, ExtendedIntValues>instead.Yet another solution is to use
std::optional:All these solution sacrifice space with
std::variantsacrificing usability.Another solution that just sacrifices 3 normal
intvalues is to have just oneintand have some values represent the special cases:Internally:
std::numeric_limits<int>::min()beeingNegInfstd::numeric_limits<int>::max() - 1isInfstd::numeric_limits<int>::max()isNaNOr something like that, but that would have to be made completely transparent. Extra special care would have to be made to arithmetic operations.
Another solution is to realize there is already a type (or two) that natively support these values and go with
floatordouble. This would sacrifice precision.Once you have
ExtendedIntfigured out then just follow the wiki algorithm:Minimum interface for the above to work:
Finally please note this from wikipedia:
So you would ultimately have to work only with
MultiIntervalwhere an interval is aMultiIntervalwith just one interval.