12
votes

Static analysis tool I use raises a warning for this code :

uint16 var1 = 1U;
uint16 var2 = ~var1;

I check MISRA C 2004 rules and I find 10.5 rule :

If the bitwise operators ~ and << are applied to an operand od underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand.

Ok, it's not a problem, implicit cast is applied (I think "cast" means implicit or explicit cast). But 10.1 rule says :

The value of an expression of integer type shall not be implicitly converted to a different underlying type the expression is complex.

An previous example of complex operation are : ~u16a

I change my code to :

uint16 var1 = 1U;
uint16 var2 = (uint16) ~var1;

And I obtain another warning : I think conversion of int negative value to unsigned int value not safe. I check C99 standard (ISO C99) § 6.3.1.3 but I don't understand if conversion of int to unsigned short are clearly specified.

In EmbeddedGurus article I read :

c = (unsigned int) a; /* Since a is positive, this cast is safe */

My questions :

  1. Have explicit conversion from signed int to unsigned short unspecified behavior ?
  2. If yes, how to use complement operator with unsigned short in safe way ?
2
here are some nice answers explaining numeric promotionchouaib

2 Answers

17
votes

The operands of the arithmetic and bitwise operators always undergo the standard promotions before the value is computed. Anything shorter than an int is promoted to either int or unsigned int, depending on the platform (i.e. depending on whether int can represent all values of the type that's being promoted).

On your platform, uint16_t is standard-promoted to int, since your int can represent all values of a uint16_t. Then the bitwise negation is applied to that int value, which is the cause of the problem.

To get a deterministic result independent of the platform, convert the value to an unsigned int yourself:

 uint16_t var2 = (uint16_t) ~((unsigned int) var1);

Note that this is always correct, since unsigned int is required to be able to represent all the values of a uint16_t.

5
votes
  1. Have explicit conversion from signed int to unsigned short unspecified behavior ?

The conversion from signed to unsigned values is well specified it happens via modulo arithmetic is covered by section 6.3.1.3 Signed and unsigned integers from the C99 draft standard:

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.49)

So for your case the negative number would be converted by repeatedly adding:

UMAX + 1 

to the negative result until it is range of the unsigned type you are converting to.

For example the conversion of -1 to an unsigned type always results in the max unsigned value since -1 + UMAX + 1 is always UMAX.

  1. If yes, how to use complement operator with unsigned short in safe way ?

What happens when you apply the ~ operator is that the value is bring promoted to int due to integer promotions being applied to the operand of ~. Which is covered in section 6.5.3.3 Unary arithmetic operators which says (emphasis mine):

The integer promotions are performed on the operand, and the result has the promoted type. If the promoted type is an unsigned type, the expression ~E is equivalent to the maximum value representable in that type minus E.

Given the last sentence in the quoted paragraph perhaps casting to unsigned int first may lead to more intuitive results:

uint16 var2 = ~((unsigned int)var1);

and since you are required to apply an explicit cast then you end up with this:

uint16 var2 = (uint16) ~((unsigned int)var1);