Suppressing warning: comparison is always false due to limited scope of data type

纵然是瞬间 提交于 2020-01-16 17:35:13

问题


I have a templated function for returning the number of digits in a number:

    template <typename R>
    static inline unsigned count(const R num)
    {
        if(num < 10)                    return 1;
        else if (num < 100)             return 2;
        else if (num < 1000)            return 3;
        else if (num < 10000)           return 4;
        else if (num < 100000)          return 5;
        else if (num < 1000000)         return 6;
        else if (num < 10000000)        return 7;
        else if (num < 100000000)       return 8;
        else if (num < 1000000000)      return 9;
        else if (num < 10000000000ULL)          return 10;
        else if (num < 100000000000ULL)         return 11;
        else if (num < 1000000000000ULL)        return 12;
        else if (num < 10000000000000ULL)       return 13;
        else if (num < 100000000000000ULL)      return 14;
        else if (num < 1000000000000000ULL)     return 15;
        else if (num < 10000000000000000ULL)    return 16;
        else if (num < 100000000000000000ULL)   return 17;
        else if (num < 1000000000000000000ULL)  return 18;
        else if (num < 10000000000000000000ULL) return 19;
        else                                    return 20;
    }

However when I compile (GCC) I get the following warning:

warning: comparison is always true due to limited range of data type

I understand why I get this repeatedly but I'm not sure how to suppress/avoid it.

Any thoughts?


回答1:


If you don't care about user-defined integer types (and evidence suggests that you don't care about negative values, either), just define one function which takes the largest type that you care about:

inline unsigned count(unsigned long long num){
    if(num < 10)                    return 1;
    else if (num < 100)             return 2;
    // blah blah
    else return 20;
}

If you call it with a signed short or whatever, you won't get any warnings about the implicit conversion, since it's a widening.

static_cast<unsigned>(log10(num)) + 1 is also worth profiling.




回答2:


You can avoid the warning be rewriting your method as

unsigned long long max = 10;
int order = 1;
while(num >= max && max * 10 > max) {
    max *= 10;
    order++;
}
return order;

I don't know whether this would be faster or slower.




回答3:


-Wtype-limits warnings can be suppressed case-by-case by wrapping comparisons with constants into a dummy binary function, which accepts operands to be compared. In this case the code above can be transformed into something like:

// Dummy function, which suppresses -Wtype-limits warnings
// where appropriate.
template <typename R>
static inline bool dummy_less(const R a, const R b)
{
  return (a < b);
}

template <typename R>
static inline unsigned count(const R num)
{
    if (dummy_less(num, 10))        return 1;
    else if (dummy_less(num, 100))  return 2;
    // ...
    else                            return 20;
}

The compiler should easily propagate constants into dummy functions.




回答4:


You could specialize or overload count() for types that aren't as large as unsigned long long.




回答5:


Apparently the type of your data (R) has smaller size than some of your constants in if()'s B.t.w. wouldn't it be better to have while(num/10) loop?

Example,

template <typename R>
unsigned count(R num)
{
    size_t i = 0;
    while(num /= 10)
        ++i;
    return i;
}



回答6:


You could add the -Wno-type-limits to disable that specific warning. However, since it's a template function, you can't isolate that warning flag to a particular translation unit, so you'd have to enable that flag for your entire project, which may be undesirable.




回答7:


May be a static_cast would help (yes it looks horrid, but this approach hmm...)?

    else if (static_cast<unsigned long long>(num) < 10000000000ULL)          return 10;
    else if (static_cast<unsigned long long>(num) < 100000000000ULL)         return 11;
    else if (static_cast<unsigned long long>(num) < 1000000000000ULL)        return 12;
    else if (static_cast<unsigned long long>(num) < 10000000000000ULL)       return 13;
    else if (static_cast<unsigned long long>(num) < 100000000000000ULL)      return 14;
    else if (static_cast<unsigned long long>(num) < 1000000000000000ULL)     return 15;
    else if (static_cast<unsigned long long>(num) < 10000000000000000ULL)    return 16;
    else if (static_cast<unsigned long long>(num) < 100000000000000000ULL)   return 17;
    else if (static_cast<unsigned long long>(num) < 1000000000000000000ULL)  return 18;
    else if (static_cast<unsigned long long>(num) < 10000000000000000000ULL) return 19;

etc.




回答8:


Perhaps generate the right amount of comparisons at compile-time?

#include <limits>

template <class T, int power>
struct pow10
{
    static const T value = 10 * pow10<T, power - 1>::value;
};

template <class T>
struct pow10<T, 0>
{
    static const T value = 1;
};

template <class T, int power_of_ten, bool recurse>
struct digit_counter
{
    unsigned count(T value) const
    {
        if (value < pow10<T, power_of_ten>::value) return power_of_ten;
        else return digit_counter<T, power_of_ten + 1, power_of_ten < std::numeric_limits<T>::digits10>().count(value);
    }
};

template <class T, int power_of_ten>
struct digit_counter<T, power_of_ten, false>
{
    unsigned count(T ) const
    {
        return   std::numeric_limits<T>::digits10 + 1;
    }
};

template <class T>
unsigned count(T value)
{
    return digit_counter<T, 1, (std::numeric_limits<T>::digits10 > 1)>().count(value);
}

When optimized, it should yield pretty much identical binary to the original.




回答9:


// Beware. brain-compiled code ahead!
namespace {
  inline unsigned count_long_long(unsigned long long num)
    if (num < 10000000000ULL)          return 10;
    else if (num < 100000000000ULL)         return 11;
    else if (num < 1000000000000ULL)        return 12;
    else if (num < 10000000000000ULL)       return 13;
    else if (num < 100000000000000ULL)      return 14;
    else if (num < 1000000000000000ULL)     return 15;
    else if (num < 10000000000000000ULL)    return 16;
    else if (num < 100000000000000000ULL)   return 17;
    else if (num < 1000000000000000000ULL)  return 18;
    else if (num < 10000000000000000000ULL) return 19;
    else                                    return 20;
  }
  template <typename R>
  inline unsigned count_long_long(const R num) {return 20;}
}

template <typename R>
inline unsigned count(const R num)
{
  if(num < 10)                    return 1;
  else if (num < 100)             return 2;
  else if (num < 1000)            return 3;
  else if (num < 10000)           return 4;
  else if (num < 100000)          return 5;
  else if (num < 1000000)         return 6;
  else if (num < 10000000)        return 7;
  else if (num < 100000000)       return 8;
  else if (num < 1000000000)      return 9;
  else return count_long_long(num);
}



回答10:


Since it's an inline function, why don't you trust the compiler a little and make it

static inline unsigned count(const R unsigned long long);

which should then promote its argument of whatever integer type to unsigned long long (standards pedants pick me up on this, but I assume it would) and run the code as given, which gives the same result as it would in your templated case.

In practice the compiler should (test it if you're bothered) remove the unnecessary conditions if they spot the input to have restricted range, which is in any case what you're hoping for in the templated case. The additional cost over templating is one promotion from <num> to unsigned long long, assuming, again, that your compiler's an idiot.




回答11:


I have an issue with your approach.

Your code is not optimized, because you do a linear search instead of a binary search.

A quick example, say that your number is between 0 and 65535 (inclusive) you should probably use the following algorithm:

if num < 1000:
  if num < 100:
     if num < 10: return 1
     return 2
  return 3

if num < 10000: return 4
return 5

This is a binary search, there is at most 3 comparisons in the worst case (numbers of 1 or 2 digits) and all the others are only 2 comparisons.

This implies that there is information in the size of your integer, that should not be discarded thoughtlessly.

namespace detail
{
  template <int Size> struct number_bits {};

  template <typename Unsigned>
  number_bits< sizeof(Unsigned)*8 > count_number_bits(Unsigned)
  {
    return number_bits< sizeof(Unsigned)*8 >();
  }

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<8>);

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<16>);

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<32>);

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<64>);

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<128>);

  template <typename Unsigned>
  unsigned number_digits_helper(Unsigned t, number_bits<256>);

} // namespace detail


template <typename Unsigned>
unsigned number_digits(Unsigned t)
{
  static_assert(!std::numeric_limits<Unsigned>::is_signed, "t is signed");
  return detail::number_digits_helper(t, detail::count_number_bits(t));
}

And now, you can fully optimize each of the routines per number of bits of the unsigned type (I know, there is no real reason there would be more than one unsigned type per size, would it ?)

For optimizing when the size is known, I recommend you have a look at Bit Twiddling Hacks which is all about micro optimizations.



来源:https://stackoverflow.com/questions/4618399/suppressing-warning-comparison-is-always-false-due-to-limited-scope-of-data-typ

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!