Cast from "int" to "unsigned short" after applying bitwise operator "~"

11,835

Solution 1

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.

Solution 2

  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);
Share:
11,835

Related videos on Youtube

no_name
Author by

no_name

Updated on September 15, 2022

Comments

  • no_name
    no_name over 1 year

    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 ?
  • T.C.
    T.C. over 9 years
    The problem is that the value of ~1 can vary depending on int's representation (e.g., -1 in one's complement, -2 in two's complement, etc.), so the result when converted to unsigned short may also be different.
  • Shafik Yaghmour
    Shafik Yaghmour over 9 years
    @T.C. I removed that statement, as you point out out it was too broad
  • supercat
    supercat over 9 years
    What would be the correct way to write the code if the type in question had been int32_t rather than int16_t, since on some compilers long might be 64 bits, while on others int would be 16?
  • Kerrek SB
    Kerrek SB over 9 years
    int16_t can always be promoted to int, should it actually need promotion, so you don't have to write anything. (That said, bitwise operations on signed integers are generally ill-advised.)
  • supercat
    supercat over 9 years
    Hence my (intended) question about how one should write the code if the type had been uint32_t rather than uint16_t. Casting to unsigned int would fail on platforms where unsigned int is 16 bits, and casting to unsigned long could result in value truncation if that type is 64 bits. IMHO, for C to be decent language for writing portable code, it needs to include fixed-sized unsigned types which would not promote.
  • Kerrek SB
    Kerrek SB over 9 years
    @supercat: Ask it as a separate question.
  • no_name
    no_name over 9 years
    @Kerrek SB : Ok, I avoid integral promotion with explicit cast to a widening unsigned integer type. But use of "int" is a violation of 6.3 of MISRA rule (use of typedef that indicate size and signedness). Moreover, I think add precision of size is not a good idea because it would make the code not independant of the plateform. But my first question is : explicit conversion from signed int to unsigned int is a non-safe conversion ? 6.10.3 says this a dangerous conversion because that implies loss of sign... but it's my goal, and explicit conversion does this means, no ?
  • Kerrek SB
    Kerrek SB over 9 years
    @JérômeBurlando: I don't understand what "safe" means, so I cannot answer that. But as I said before, you should a) not use bit operations on signed integers because that's sort of meaningless, and b) not convert between signed and unsigned for no reason. The answer I gave solves all of those problems, though I'm not entirely sure if there's a MISRA compliant way. Maybe convert to uintmax_t, though that's gratuitous.
  • no_name
    no_name over 9 years
    @Kerrek SB: If I understand, when I use bitwise operators on small interger type, it's a good idea to convert explicitly to widening unsigned interger type in order to avoid integral promotion resulting using signed integer type ?
  • Kerrek SB
    Kerrek SB over 9 years
    @JérômeBurlando: Yes, that sounds right. That's a good summary.