diff --git a/PRECISION.md b/PRECISION.md index 62f044f..8833c6c 100644 --- a/PRECISION.md +++ b/PRECISION.md @@ -33,6 +33,16 @@ Here we explain the logic behind the implementation of precision inference in ea abs is a function that is equal to the identity function on positive values and its opposite on negative values. As such, its output precision should be the same as its input precision. +### Integers + +When applied to integers, Abs is implemented using the `std::abs` C++ function. +This function has undefined behaviour when applied to `INT_MIN`, since `-INT_MIN = INT_MAX + 1` is not representable. +We chose to interpret this fact as the indication that `INT_MIN` is out of bounds for Abs. +We deal with it the same way we deal with other domain violations: +we restrict the input interval to legal values and apply Abs to the restricted interval. + +`Abs([INT_MIN; x]) = [0; INT_MAX]` + ## Acos acos is the reciprocal of the cosine function. @@ -66,6 +76,8 @@ Addition is a binary operator, and the above method, designed for unary, real fu Let us consider the addition of arguments $x$ and $y$, with respective precisions $l_x$ and $l_y$. Without loss of generality, let's assume that $l_x < l_y$, ie, $x$ is represented with more precision than $y$. In order to be able to distinguish the two sums $x + y$ and $x' + y$, where the operands $x$ and $x'$ only differ by their last bit, bits up to $l_x$ have to be conserved in the output. Thus, we establish that $l' = min(l_x, l_y)$ is the coarsest output precision that still respects pseudo-injectivity. +### Integers + Wrapping situations due to integer overflow have to be taken care of. In the case where both operands are integers, if their sum is higher than the biggest representable integer or lower than the smallest representable integer, the default behaviour is for the value to "wrap" around and get to the other end of the integer range. This results in the output interval of the operation being `[INT_MIN; INT_MAX]`. ## And diff --git a/interval/check.cpp b/interval/check.cpp index 48ee74f..8490ae9 100644 --- a/interval/check.cpp +++ b/interval/check.cpp @@ -51,6 +51,8 @@ void check(const std::string& testname, const itv::interval& exp, const itv::int { if (exp == res) { std::cout << "OK: " << testname << " " << exp << " = " << res << std::endl; + if (exp.lsb() != res.lsb()) + std::cout << "\t But precisions differ by " << exp.lsb() - res.lsb() << std::endl; } else { std::cout << "ERR:" << testname << " FAILED. We got " << exp << " instead of " << res << std::endl; } diff --git a/interval/intervalAbs.cpp b/interval/intervalAbs.cpp index 685d25e..19fb1c4 100644 --- a/interval/intervalAbs.cpp +++ b/interval/intervalAbs.cpp @@ -32,9 +32,14 @@ interval interval_algebra::Abs(const interval& x) if (x.lo() >= 0) { return x; } + + if (x.lsb() >= 0 and x.lo() <= (double)INT_MIN){ + return {0, (double)INT_MAX, x.lsb()}; + } if (x.hi() <= 0) { return {-x.hi(), -x.lo(), x.lsb()}; } + return {0, std::max(std::abs(x.lo()), std::abs(x.hi())), x.lsb()}; } @@ -44,5 +49,9 @@ void interval_algebra::testAbs() 10, 10000, "abs", interval(-10, 10, 0), [](double x) { return std::abs(x); }, &interval_algebra::Abs); analyzeUnaryMethod( 10, 10000, "abs", interval(-10, 10, -15), [](double x) { return std::abs(x); }, &interval_algebra::Abs); + // testing the behaviour on integers + check("abs", Abs(interval(INT_MIN, INT_MIN, 0)), interval(0, INT_MAX, 0)); + check("abs", Abs(interval(INT_MIN, INT_MIN, 5)), interval(0, INT_MAX, 5)); + check("abs", Abs(interval(INT_MIN, INT_MIN, -5)), interval(-(float)INT_MIN, -(float)INT_MIN, -5)); } } // namespace itv \ No newline at end of file