Checking if an input is within its range of limits in C++

25.7k Views Asked by At

I need to create multiple functions that will check if an input is valid or not. Here are some of my codes:

bool IsValidRange(signed char s)
{
    bool isValid = true;
    if (!((s>=SCHAR_MIN)&&(s<=SCHAR_MAX)))
    {
        isValid = false;
    }
    return isValid;
}

bool IsValidRange(int s)
{
    bool isValid = true;
    if (!((s>=INT_MIN)&&(s<=INT_MAX)))
    {
        isValid = false;
    }
    return isValid;
}

I'm using the header limits.h for this. Am I doing this the right way? Please take note that I'm just a beginner. I hope you all will understand. Thank you!

5

There are 5 best solutions below

5
On

First off, you don't need all those convolutions with booleans. Just return the expression directly:

return (s >= SCHAR_MIN) && (s <= SCHAR_MAX);

Second, you should realize that both your functions always yield true: by definition, a signed char is always in the range [SCHAR_MIN, SCHAR_MAX], and an int is always in the range [INT_MIN, INT_MAX].

But if you chose different bounds, this would indeed be the way to do it (taking into account my first remark).

As EdS. suggested, you could use a templated solution to reduce the number of functions/overloads you need:

template<class T>
bool check_range(T value, T min, T max) {
    return (value >= min) && (value <= max);
}
1
On
#include <limits>

template<typename ValueType, typename RangeType >
bool IsInRange( ValueType value ) {
    return (value >= numeric_limits<RangeType>::min()) &&
           (value <= numeric_limits<RangeType>::max());
}

This will work for integral data types except for floating point values. numeric_limits<T>::min() will return the minimum normalized positive value for floating point types. C++11 introduced numeric_limits<T>::lowest() to resolve the issue.

0
On

The answer is simple. If you define the function's argument as a specific type, then the compiler forces the input in the declared type's range limits.

So your code always returns the true, and the checking process practically is not working.

For solving this kind of problem, You can use a simple technique. How? Let me explain: You can choose an argument with limits larger than the actual value you decided. For example: if the argument is an integer, define it as a long type.

Now, your checking process will work. Why?

Because the number you declared can accept the more considerable value, but you will finally decide how much the input value should be in the checking process.

This trick can be expensive in compilation and memory usage, and it is slightly slippery for the novice. But for non-complex functions and procedures and the limited number of arguments or variables work well.

Check the following code in your computer and see the result:

long i = 0L;
int left_limit = std::numeric_limits<int>::min();
int right_limit = std::numeric_limits<int>::max();

std::cout << "Enter an integer: ";
std::cin >> i;
if (i < left_limit || i > right_limit) {
        std::cout << "You have reached to out of limits." << std::endl;
        return false;
}
0
On

This is an implementation based on the answer by RadioKat, but it suppresses compiler warnings like:

warning: pointless comparison of unsigned integer with zero
warning: integer conversion resulted in a change of sign

by using template meta programming:

#include <limits>

template< typename T_Range, typename T_Value, bool T_RangeSigned, bool T_ValueSigned >
struct InIntegerRange;

template< typename T_Range, typename T_Value >
struct InIntegerRange< T_Range, T_Value, false, false >
{
    bool operator()( T_Value const & x )
    {
        return x >= std::numeric_limits< T_Range >::min() &&
               x <= std::numeric_limits< T_Range >::max();
    }
};

template< typename T_Range, typename T_Value >
struct InIntegerRange< T_Range, T_Value, false, true >
{
    bool operator()( T_Value const & x )
    {
        return x >= 0 && x <= std::numeric_limits< T_Range >::max();
    }
};

template< typename T_Range, typename T_Value >
struct InIntegerRange< T_Range, T_Value, true, false >
{
    bool operator()( T_Value const & x )
    {
        return x <= std::numeric_limits< T_Range >::max(); /* x >= 0 is given */
    }
};

template< typename T_Range, typename T_Value >
struct InIntegerRange< T_Range, T_Value, true, true >
{
    bool operator()( T_Value const & x )
    {
        return x >= std::numeric_limits< T_Range >::min() &&
               x <= std::numeric_limits< T_Range >::max();
    }
};

template< typename T_Range, typename T_Value >
inline bool inRange( T_Value const & x )
{
    if( std::numeric_limits< T_Range >::is_integer )
    {
        return InIntegerRange< T_Range, T_Value,
                               std::numeric_limits< T_Range >::is_signed,
                               std::numeric_limits< T_Value >::is_signed >()( x );
    }
    else
    {
        return ( x > 0 ? x : -x ) <= std::numeric_limits< T_Range >::max() ||
               ( std::isnan(x) && std::numeric_limits< T_Range >::has_quiet_NaN ) ||
               ( std::isinf(x) && std::numeric_limits< T_Range >::has_infinity );
    }
}
2
On

the template above will not work for the mixture of signed and unsigned types, in addition to floats.

template<typename RangeType, typename ValueType >
bool IsInRange( ValueType value ) 
{
    if( ! numeric_limits<RangeType>::is_integer )
    {
        return  (value > 0 ? value  : -value) <= numeric_limits<RangeType>::max(); 
    }

    if ( numeric_limits<RangeType>::is_signed == 
         numeric_limits<ValueType>::is_signed )
    {
        return value >= numeric_limits<RangeType>::min() &&
               value <= numeric_limits<RangeType>::max();
    }
    else if( numeric_limits<RangeType>::is_signed )
    {
        return value <= numeric_limits<RangeType>::max();
    }
    else
    {
        return value >= 0 && value <=  numeric_limits<RangeType>::max();
    }
}