4

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.

Sir2B
  • 1,029
  • 1
  • 10
  • 17
  • 1
    You can call it with 64 bit integer `IsHysteresisExceeded(-2147483648, 2147483647, 10)`. If that is still not enough, you need `double`. – pptaszni Aug 30 '22 at 14:13
  • https://stackoverflow.com/questions/199333/how-do-i-detect-unsigned-integer-overflow the answer mentioning `` might help you. – Aedoro Aug 30 '22 at 14:14
  • 1
    Ignore the template for now. How would you write the code for `bool IsHysteresisExceeded(int a, int b, int hysteresis)`? – Eljay Aug 30 '22 at 14:32
  • @pptaszni yeah that would help for 32 Bit. But for 64 Bit running with double will fail for big numbers and small hysteresis – Sir2B Aug 30 '22 at 14:42
  • @Eljay two options: using 64 Bit Integers or cast (ValueMax - ValueMin) to unsigned. – Sir2B Aug 30 '22 at 14:44
  • IMO you mean threshold, not hysteresis. –  Aug 30 '22 at 18:18

3 Answers3

2

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.

Bartosz Charuza
  • 491
  • 1
  • 7
0

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 substract both "positions" to get the range between them, and compare it 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) {
    long double posA = (long double) ValueA / std::numeric_limits<T>::max();
    long double posB = (long double) ValueB / std::numeric_limits<T>::max();

    if (std::fabs(posA - posB) > 1)
        return true; //ValueMax - ValueMin would 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;
}
Ripi2
  • 7,031
  • 1
  • 17
  • 33
0

I was hoping that this version would compile down efficiently, but alas, the C++ compiler on my machine is unable to merge the two branches. Posting anyway because it uses only +, -, < and a default constructor for 0.

#include <algorithm>
#include <tuple>

template <typename T> bool IsHysteresisExceeded(T a, T b, T h) {
  std::tie(a, b) = std::minmax(a, b);
  return a < T{} ? h + a < b : h < b - a;
}

bool test(int a, int b, int h) { return IsHysteresisExceeded(a, b, h); }
David Eisenstat
  • 64,237
  • 7
  • 60
  • 120