Compare two integers for equality in C++ with unknown int types

2

Basically what I want is something simple, a function:

template<typename A, typename B>
is_the_same_value(A a, B b)
{
    // return true if A is the same value as B
}

This seemingly simple question is is hard as using return a == b fails for int/unsigned mix, e.g.

is_the_same_value(-1, 0xffffffff)

would return true. clang/gcc warn about this: comparison of integers of different signs

Using something like a == b && ((a > 0) == (b > 0)) works but still triggers the compiler warning.

c++
asked on Stack Overflow Dec 29, 2019 by plaisthos • edited Dec 29, 2019 by plaisthos

5 Answers

2

You can use std::common_type to remove the warning.

#include <type_traits>
template<typename A, typename B>
bool is_numbers_equal(A a, B b) {
    using C = typename std::common_type<A, B>::type;
    return (a > 0) == (b > 0) && static_cast<C>(a) == static_cast<C>(b);
    // or maybe `... && C(a) == C(b);`
}

Well, this looks fun to optimize it for the compiler for types that both are the "same":

#include <type_traits>

template<typename A, typename B>
typename std::enable_if<
    std::is_integral<A>::value &&
    std::is_integral<B>::value &&
    (std::is_signed<A>::value ^ std::is_signed<B>::value)
, bool>::type
is_numbers_equal(A a, B b) {
    using C = typename std::common_type<A, B>::type;
    return (a >= 0) == (b >= 0) && 
        static_cast<C>(a) == static_cast<C>(b);
}

template<typename A, typename B>
typename std::enable_if<
    ! (
        std::is_integral<A>::value &&
        std::is_integral<B>::value &&
        (std::is_signed<A>::value ^ std::is_signed<B>::value)
    )
, bool>::type
is_numbers_equal(A a, B b) {
    return a == b;
}
answered on Stack Overflow Dec 29, 2019 by KamilCuk • edited Dec 29, 2019 by KamilCuk
1

For value based comparisons where no implicit conversions (signed/unsigned etc) are allowed, checking for type equality could be an option:

#include <type_traits>

template<typename A, typename B>
bool is_the_same_value(const A&  a, const B& b)
{
    if constexpr(!std::is_same_v<A,B>) return false;
    else return a == b;
}
answered on Stack Overflow Dec 29, 2019 by Ted Lyngmo • edited Dec 29, 2019 by Ted Lyngmo
0

Can't you use std::is_integral to check whether both types are int? Then convert them to a signed long long and then compare them.

answered on Stack Overflow Dec 29, 2019 by JMRC
0

You can compare memories:

template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
    return std::memcmp(&a,&b,sizeof(A)) == 0;
    // return true if A is the same value as B
}
answered on Stack Overflow Dec 29, 2019 by alirakiyan
0

This does not look like a hard problem to me. There are enough complications so I would not call the result "simple", but it's far from complex. Just deal with the identified issues one at a time, and make sure things are symmetric with respect to the two parameters.

Issue 1: converting a negative value to an unsigned type is bad.
Issue 2: comparing different types can cause compiler warnings.

template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
    // Address issue 1 by returning false if exactly one of the parameters
    // is negative.
    if ( a < 0 && b >= 0 )
        return false;
    if ( b < 0 && a >= 0 )
        return false;
    // At this point, either both a and b are negative (hence A and B are signed types)
    // or both are non-negative (hence converting to an unsigned type is fine).

    // Address issue 2 by explicitly casting. Make sure you cast both
    // ways to keep things symmetric.
    return a == static_cast<A>(b)  &&
           b == static_cast<B>(a);
}

Sure, there are clever ways to make this code more compact (and less readable), but isn't the compiler supposed to be able to do those optimizations for you?

answered on Stack Overflow Dec 29, 2019 by JaMiT

User contributions licensed under CC BY-SA 3.0