I want to implement a template function, which detects if the difference of ValueA and ValueB is bigger than a given hystersis. e.x.
- ValueA=5, ValueB=7, Hystersis=1 -> true
- ValueA=5, ValueB=7, Hystersis=3 -> false
- ValueA=-5, ValueB=1, Hystersis=7 -> false
So I implemented this function:
template<typename T>
bool MyClass::IsHysteresisExceeded(T ValueA, T ValueB, T Hysteresis) {
T ValueMax = std::max(ValueA, ValueB);
T ValueMin = std::min(ValueA, ValueB);
return (ValueMax - ValueMin) > Hysteresis;
}
But with the following parameters this function returns false when I expected true as result.
IsHysteresisExceeded<int>(-2147483648, 2147483647, 10)
I know that a integer overflow occurs while subtracting, but I did not find an elegant solution yet.
CodePudding user response:
I have the following solution for integers:
template<typename T>
bool IsHysteresisExceeded(T ValueA, T ValueB, T Hysteresis) {
T ValueMax = std::max(ValueA, ValueB);
T ValueMin = std::min(ValueA, ValueB);
assert(Hysteresis >= 0);
T underflowRange = std::numeric_limits<T>::min() Hysteresis;
bool underflow = underflowRange > ValueMax;
return !underflow && (ValueMax - Hysteresis > ValueMin);
}
The trick is to detect the underflow. If it happens you may be sure ValueMin
is in range <ValueMax,std::numeric_limits<T>::min()>
and
(ValueMax - Hysteresis) < std::numeric_limits<T>::min() <= ValueMin
I posted the code on godbolt.org
Edit: My previous answer used a very popular approach and was also wrong. I proposed to detect the underflow like:
T lowBound = ValueMax - Hysteresis;
bool underflow = lowBound > ValueMax;
Although it produces expected results on the architectures i know, it is an undefined behavior.
CodePudding user response:
One way to detect possible overflow is to use some indicator of "how far" from limits is a value. I use a simple division, which wants to normalize vale values in the range [-1,1].
Then I add both "positions" and compare with a valid range, this is, 1
:
#include <limits>
#include <math.h>
#include <iostream>
template<typename T>
bool IsHysteresisExceeded(T ValueA, T ValueB, T Hysteresis) {
double posA = (double) ValueA / std::numeric_limits<T>::max();
double posB = (double) ValueB / std::numeric_limits<T>::max();
if (std::fabs(posA - posB) > 1)
return true; //overflow
T ValueMax = std::max(ValueA, ValueB);
T ValueMin = std::min(ValueA, ValueB);
return (ValueMax - ValueMin) > Hysteresis;
}
int main()
{
std::cout << (IsHysteresisExceeded<int>(-2147483648, 2147483647, 10) ? "Exceeded" : "In range") << std::endl;
}