1

GIVEN:

A type defined as TheValueT that may be arbitrarily configured, e.g. as uint8_t or int64_. Let there be some code:

TheValueT   x = ...;

... do something to 'x' ...

if( x < 0 ) {
   /* Do something. */
}

PROBLEM:

It happens that if TheValueT is defined as an unsigned type, the compiler complains about 'condition always true because of limited range of type ...'.

QUESTION:

How can the compiler warning be avoided whilst letting TheValueT still be of arbitrary integer type? The solution should be applicable to the widest range of C compilers.

Frank-Rene Schäfer
  • 3,182
  • 27
  • 51

3 Answers3

2

A simple and safe way to write your test would be this:

TheValueT x = /* ... */;

if (x < 1 && x != 0) {
    // do something
}

It is possible that a clever enough compiler would warn about that anyway, but the same is true of any correct alternative that can be written to cover all possible integer types (including extension types). That does work around the warning in my implementation.

No alternative requiring an arithmetic computation involving the value of x definedly produces the correct result in all cases -- these run into problems with values of x at the extremes of its or other types' ranges.

This does assume that TheTypeT must be an integer type. If floating types are a possibility, too, then your best bet may be to just live with the warning, or to use a compiler flag to turn off that particular warning during production builds.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • This is by far the most elegant, most comprising solution! – Frank-Rene Schäfer Jun 06 '19 at 13:29
  • 1
    Since `x` can be "arbitrarily configured", what if `x` is a `double`, and has value `0.25`?. It would pass your test (`x < 1 && x !=0`) and treat `0.25` as if it were a negative value. – abelenky Jun 06 '19 at 14:03
  • Yes, @abelenky. I address this issue in the last paragraph of the answer: the proffered solution explicitly addresses integer types only. – John Bollinger Jun 06 '19 at 14:14
  • My answer handles floating-point types too. There is no need to just "live with the warning". – abelenky Jun 06 '19 at 15:20
1

Perhaps a Generic solution?

#include <stdio.h>
#include <stdlib.h>

#define less_than_zero(x) _Generic((x) + 0, \
  int: (x) < 0, \
  long: (x) < 0, \
  long long: (x) < 0, \
  default: (x) * 0 \
  )

#if 1
int main(void) {
  short sh = -1;
  int i = -1;
  long long ll = -1;
  unsigned short us = -1u;
  unsigned u = -1u;
  unsigned long long ull = -1u;

  if (less_than_zero(sh)) puts("sh");
  if (less_than_zero(i)) puts("i");
  if (less_than_zero(ll)) puts("ll");
  if (less_than_zero(us)) puts("us");
  if (less_than_zero(u)) puts("u");
  if (less_than_zero(ull)) puts("ull");
  return 0;
}

No condition always true because of limited range of type warning.
Output

sh
i
ll
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Variation on a theme. Works for FP too.

if (x <= 0 && x != 0) {
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Certainly, it has some charm to it. I could imagine, though, that constant value reduction of some compilers might be able to figure that out. John's solution with the 'x<1' contains a '-1' operation. Since for unsigned types the result is something void, constant reduction might be prevented from happening. – Frank-Rene Schäfer Jun 07 '19 at 07:33