4

Consider this code:

uint16_t a = ~ ( uint16_t ) 0;
int16_t  b = ~ ( int16_t ) 0;
printf (
    "%d %d %d %d\n",
    a  == ~ ( uint16_t ) 0,
    a  == ( uint16_t ) ( ~ ( uint16_t ) 0 ),
    b  == ~ ( int16_t ) 0,
    b  == ( int16_t ) ( ~ ( int16_t ) 0 )
);

The output is:

0 1 1 1

GCC throws a warning about a == ~ ( uint16_t ) 0:

comparison is always false due to limited range of data type [-Wtype-limits]

Why is the bitwise "not" operator trying to return a signed value? How can I prevent that?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
puchu
  • 3,294
  • 6
  • 38
  • 62

2 Answers2

10

Why bitwise "not" operator is trying to return signed value?

Because no operator works on types smaller than int; smaller types (including uint16_t if int has more than 16 bits) are promoted when used as operands. If all values of the original type are representable by int, as they are here, then the promotion will be to int.

How can I prevent that?

You can't; that's how the language works. You'll have to convert the result of the operator to the type you want, either implicitly as you do when initialising a, or explicitly with a cast. Note that you don't need the cast before applying ~; but for maximum portability, you should stick to unsigned types when doing bitwise logic:

uint16_t a = ~0u;
printf("%d\n", a == (uint16_t)~0u);  // Should print 1
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    This is unclear about why the `printf` should print “1”. Is it intended that `a` be initialized with the expression shown here, `(uint16_t)~0`, or the expression in the original code, `~ ( uint16_t ) 0`? If the latter, “1” is not necessarily printed. The latter expression always yields 65535 in `a`, but the former may have the value 0, 1, or 65535, depending on the C implementation. It would be better to use `~0u` rather than `~0`. – Eric Postpischil May 11 '13 at 18:02
  • @EricPostpischil: You're right, of course; it's easy to forget that the language supports exotic representations of signed integers when such things have been (more or less) nonexistent within my lifetime. I've added a note that unsigned types should be preferred, just in case someone blindly follows my advice while programming a UNIVAC. – Mike Seymour May 11 '13 at 22:44
3

Sign is a concept that we lay on top of bit-patterns. Bit-wise not (~) concerns only the bit-pattern and not the sign of the value. The result of notting the signed an unsigned value is identical.

Having said that looking at the C standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf (draft version available for free). Section 6.3.1.4, page 51:

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. (58) All other types are unchanged by the integer promotions.

I take this to mean, char and short types will be promoted to an int (or unsigned int depending on the size) when we're actually operating on them. This makes sense because we want the operations to be done as quickly as possibly, so we should target the native size of the machine.

Given this we can see what is actually happening. The machine will perform all operations with an 'int' size, because both the operands to '==' and '~' can fit in an int field which I'm assuming in your machine is 32 bits.

Now the first thing to look at is the value of 'a'. We take 0, we not it and get 0xFFFFFFFF. We assign this to a uint16_t value and get 0xFFFF. When we're ready to do the comparison we load the 0xFFFF, realize the value is unsigned and zero extend it to 0x0000FFFF. For the value of value of 'b', everything is the same except when we read the 0xFFFF for the comparison we sign extend it to 0xFFFFFFFF. Now for your cases:

  1. Notting the zero gives 0xFFFFFFFF and that compared to 0x0000FFFF will fail.
  2. We took our 0xFFFFFFFF, chopped it to 0xFFFF and then zero extended it to 0x0000FFFF, giving the same value as 'a'.

And so on.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ali-hussain
  • 511
  • 3
  • 9