implicit signed to unsigned conversion

Thread Starter

bug13

Joined Feb 13, 2012
2,002
My code with warning:
Code:
uint8_t data;
// load some value into data
uint8_t converter[] = { 'h', 'e', 'l', 'l', 'o', ....};
put(converter[data >> 4U];
My code without warning:
Code:
uint8_t data;
// load some value into data
uint8_t converter[] = { 'h', 'e', 'l', 'l', 'o', ....};
put(converter[(uint8_t)(data >> 4U)];
I am using XC8 free compiler, why right shift of a unsigned data become signed??
 

WBahn

Joined Mar 31, 2012
29,976
I don't think you've given enough information to be sure. This different in your two code snippets (ignoring the syntax error) implies that the array index expression is expected to be of unsigned type; but the standard does not require this and, in fact, in the bulk of code signed integers are used for array indices.

What type of argument is the put() function expecting? I have no idea since it is not a standard C function. So check your XC8 documentation.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
I don't think you've given enough information to be sure. This different in your two code snippets (ignoring the syntax error) implies that the array index expression is expected to be of unsigned type; but the standard does not require this and, in fact, in the bulk of code signed integers are used for array indices.

What type of argument is the put() function expecting? I have no idea since it is not a standard C function. So check your XC8 documentation.
Here is more info, and the actual code the have the warning. Here is how I use it:
Code:
cc3_send_packet(data1, data2, data3, data4, data5, UART5Put);
PS: data will be assemble into uint8_t packet[], and calculate crc16 from packet[], at the same time output the data to UART in hex string.

Code:
void UART5Put(uint8_t data);

void cc3_send_packet(uint8_t type, cc3_id_t src, cc3_id_t dst, uint8_t* payload, uint8_t len, void (*cb_put)(uint8_t)){
    uint8_t packet[16];
    uint8_t index = 0;
    uint16_t crc16 = 0;

    const uint8_t converter[] = {'0', '1', '2', '3','4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    // carriage return
    cb_put('\r');

    // assemble call type
    packet[index] = type;

    // output data
    cb_put(converter[(uint8_t)(packet[index] >> 4U)]);  <<<<<<--- warning here if I don't do the casting
    cb_put(converter[packet[index] & 0x0FU]);

    // space
    cb_put(0x20);
    // other code
    // ... ...
}
 
Last edited:

WBahn

Joined Mar 31, 2012
29,976
May be a compiler artifact.

What is the exact and complete warning (and indicate which lines in the code above match any line numbers in the warning)?

You might also play with some variants to see at what point things are actually getting coerced.
 
This is not something I understand at all - quirk or bug or ??
I compile this code as a minimal example.

C:
#include <stdint.h>

void main(void) {
  uint8_t index = 0;
  index = index >> 4;  // warning
  index = index >> (uint8_t)4;  // warning
  index = index >> 4U;  // warning
  index>>=4;  // no warning
  index = (uint8_t) (index >> 4);  // no warning
  index = (uint8_t) (index >> 4U);  // no warning

  return;
}
MPLABXIDE 5.05
XC8 1.45 free

The three warnings are:
newmain.c:6: warning: (373) implicit signed to unsigned conversion
newmain.c:7: warning: (373) implicit signed to unsigned conversion
newmain.c:8: warning: (373) implicit signed to unsigned conversion

It looks like this is NOT related to some kind of signed default of a literal because 4 and 4U make no difference. It looks like it is related to the >> operator but not when the form is var>>=4.

The manual states:
2.4.11 Right-shifting Signed Values
Right-shifting a signed value will involve sign extension. This will preserve the sign of
the original value.

So, the >> operator is, in some way, treating the right side as a signed value regardless of typecasting. But, not when there is only a literal on the right side??

Like I said, I don't understand this.

Edited to add: Also, as for the manual, why is it treating the variable as a signed value at all since it is declared uint8_t
 
Last edited:

WBahn

Joined Mar 31, 2012
29,976
It sounds like there is something up with the XC8 compiler. It might just be throwing bogus warnings.

It may or may not be treating the left operand as signed. One test you can do would be to set index to a value >= 128 so that the msb is set. Then right shift it four places and see what you get.

If you set index to 192 (11000000) and right shift it four places you should get (00001100) or 12 if it is treated as unsigned and (11111100) or -4 if it is treating it as signed.
 

nsaspook

Joined Aug 27, 2009
13,079
http://ww1.microchip.com/downloads/en/DeviceDoc/50002053G.pdf

5.6 OPERATORS AND STATEMENTS The MPLAB XC8 C Compiler supports all the ANSI operators.
The exact results of some of these are implementation defined. Implementation-defined behavior is fully documented in Appendix D. Implementation-Defined Behavior. The following sections illustrate code operations that are often misunderstood as well as additional operations that the compiler is capable of performing.

5.6.1 Integral Promotion When there is more than one operand to an operator, they typically must be of exactly the same type. The compiler will automatically convert the operands, if necessary, so they do have the same type. The conversion is to a “larger” type so there is no loss of information; however, the change in type can cause different code behavior to what is sometimes expected. These form the standard type conversions. Prior to these type conversions, some operands are unconditionally converted to a larger type, even if both operands to an operator have the same type. This conversion is called integral promotion and is part of Standard C behavior. The compiler performs these integral promotions where required, and there are no options that can control or disable this operation. If you are not aware that the type has changed, the results of some expressions are not what would normally be expected. Integral promotion is the implicit conversion of enumerated types, signed or unsigned varieties of char, short int or bit-field types to either signed int or unsigned int. If the result of the conversion can be represented by an signed int, then that is the destination type, otherwise the conversion is to unsigned int.
 

WBahn

Joined Mar 31, 2012
29,976
That doesn't explain the apparent behavior. It's also not a correct (complete) description.

It says that the compiler will automatically convert the operands so that they have the same type. But not all operators require operands of the same time. The bit-shift operators are a perfect example. The C standard is pretty explicit in that if the type of the output is the same type as the left operand. Regardless of the type of the left operand, the behavior is defined (and the same) for either type of the right operand provided the value is non-negative and no greater than the width of the type of the left operand.

I would say that 5.6.1 above either neglected to address the bit-shift operators, or perhaps it is in a section of the manual that doesn't apply to bit-shift operators.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
http://ww1.microchip.com/downloads/en/DeviceDoc/50002053G.pdf

5.6 OPERATORS AND STATEMENTS The MPLAB XC8 C Compiler supports all the ANSI operators.
The exact results of some of these are implementation defined. Implementation-defined behavior is fully documented in Appendix D. Implementation-Defined Behavior. The following sections illustrate code operations that are often misunderstood as well as additional operations that the compiler is capable of performing.

5.6.1 Integral Promotion When there is more than one operand to an operator, they typically must be of exactly the same type. The compiler will automatically convert the operands, if necessary, so they do have the same type. The conversion is to a “larger” type so there is no loss of information; however, the change in type can cause different code behavior to what is sometimes expected. These form the standard type conversions. Prior to these type conversions, some operands are unconditionally converted to a larger type, even if both operands to an operator have the same type. This conversion is called integral promotion and is part of Standard C behavior. The compiler performs these integral promotions where required, and there are no options that can control or disable this operation. If you are not aware that the type has changed, the results of some expressions are not what would normally be expected. Integral promotion is the implicit conversion of enumerated types, signed or unsigned varieties of char, short int or bit-field types to either signed int or unsigned int. If the result of the conversion can be represented by an signed int, then that is the destination type, otherwise the conversion is to unsigned int.
This sounds too technical for me, so let me put it in my own words. Does it mean:

If there is more than one operand to operate on, some operands could be converted to a signed int, depending on how the compiler feels??
 

WBahn

Joined Mar 31, 2012
29,976
This sounds too technical for me, so let me put it in my own words. Does it mean:

If there is more than one operand to operate on, some operands could be converted to a signed int, depending on how the compiler feels??
The rules SHOULD be well-laid out as to when types are implicitly coerced. It's not a matter of how the compiler feels -- the compiler should follow the rules that are laid out. However, the rules are only partly specified by the language standard -- the compiler writer has some flexibility regarding some of the rules (though really not that much on this topic), but they are expected to specify what their choices were.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
The rules SHOULD be well-laid out as to when types are implicitly coerced. It's not a matter of how the compiler feels -- the compiler should follow the rules that are laid out. However, the rules are only partly specified by the language standard -- the compiler writer has some flexibility regarding some of the rules (though really not that much on this topic), but they are expected to specify what their choices were.
You are right, I guess this is just my way to saying "I am still confused, and I still don't understand"

But for the purpose of fixing my code, I think the way I fix it is fine. I do need to read more about this Integral promotion thing.
 

nsaspook

Joined Aug 27, 2009
13,079
That doesn't explain the apparent behavior. It's also not a correct (complete) description.

It says that the compiler will automatically convert the operands so that they have the same type. But not all operators require operands of the same time. The bit-shift operators are a perfect example. The C standard is pretty explicit in that if the type of the output is the same type as the left operand. Regardless of the type of the left operand, the behavior is defined (and the same) for either type of the right operand provided the value is non-negative and no greater than the width of the type of the left operand.

I would say that 5.6.1 above either neglected to address the bit-shift operators, or perhaps it is in a section of the manual that doesn't apply to bit-shift operators.
Correct, it's not a complete description of the process for integer promotions, integer conversion rank, and the usual arithmetic conversions where int is 16 bits but the hardware platform has a high performance penalty for 16 bit operand operations due to 8 bit native integer size hardware limitations. The compiler meekly warns and does the right thing for hardware when it can instead. The warning level change at 1.45 might be related to some cert recommendation.
http://www.microchip.com/stellent/groups/SiteComm_sg/documents/DeviceDoc/en555854.pdf

https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules
Noncompliant Code Example
This noncompliant code example demonstrates how performing bitwise operations on integer types smaller than int may have unexpected results:

uint8_t port = 0x5a;
uint8_t result_8 = ( ~port ) >> 4;
In this example, a bitwise complement of port is first computed and then shifted 4 bits to the right. If both of these operations are performed on an 8-bit unsigned integer, then result_8 will have the value 0x0a. However, port is first promoted to a signed int, with the following results (on a typical architecture where type int is 32 bits wide):
 
Last edited:
The issue as I am seeing it is that the warning is issued in error.
I say this because I don't see any implicit conversion in the line:
index1 = index2 >> 4U; // warning
But you get the warning. index1 is uint8_t as is index2 as is the literal 4U - there is simply no implicit conversion signed to unsigned that I can see.

In this line:
index1=index3 >>4; // warning
there is a conversion because index1 is uint8_t and index3 is int (which is signed) and this is a situation that I would expect to see the warning.

That the line below works without a warning seems to be inconsistent with the warning issued for index1 = index2 >> 4U; // warning or similar examples in an earlier post :

index1>>=4; // no warning

C:
#include <stdint.h>

void main(void) {
  uint8_t index1 = 0U;
  uint8_t index2 = 0U;
  int index3= 0;
  int index4=0;

  index1 = index2 >> 4U; // warning
  index1=index3 >>4;  // warning
  index3=index4 >>4;  // no warning
  index1>>=4;  // no warning
  index3>>=4;  // no warning
  return;
}
I will look into this some more and the suggestion to look at expected values is a good one to check.
 
Last edited:
But for the purpose of fixing my code, I think the way I fix it is fine. I do need to read more about this Integral promotion thing.
Probably and Yes :)

I like the idea of a warning that tells me a conversion has taken place because that could be a problem. I don't like it when the compiler warns me and I can't understand why I am being warned.
 

WBahn

Joined Mar 31, 2012
29,976
Probably and Yes :)

I like the idea of a warning that tells me a conversion has taken place because that could be a problem. I don't like it when the compiler warns me and I can't understand why I am being warned.
I agree strongly. My personal standard is that code is not allowed to compile with warnings without a damn good reason. But an unstated corollary to that is that warnings must be dealt with by design, not by happening. It is NOT okay to just throw around seemingly random type casts to make a warning go away -- I want to understand WHY the warning was thrown and why the code modification mitigated it. In this case I don't understand either. I've never used the XC8 compiler, but my impression based on numerous other threads is that it has some significant quirks (almost all compilers have at least a few).
 

nsaspook

Joined Aug 27, 2009
13,079
This is one of those "erring on the side of caution" warnings that's completely over engineered in XC8 to the point of being useless.

A GCC example of the warning from the 'net'. (made into an error)

# gcc -Werror -Wall -Wextra -pedantic -o signwarn signwarn.c
# gcc -Wconversion -Werror -Wall -Wextra -pedantic -o signwarn signwarn.c
signwarn.c: In function ‘sign_warn’:
signwarn.c:21:26: error: unsigned conversion from ‘int’ to ‘unsigned int’ changes value from ‘-30’ to ‘4294967266’ [-Werror=sign-conversion]
unsigned int x=20, y=-30;
^
cc1: all warnings being treated as errors
root@nmusic:~# cat signwarn.c

C:
#include <stdint.h>
#include <stdio.h>

int sign_warn(void);

int main(void) {
uint8_t index = 0;
index = index >> 4; // warning
index = index >> (uint8_t)4; // warning
index = index >> 4U; // warning
index>>=4; // no warning
index = (uint8_t) (index >> 4); // no warning
index = (uint8_t) (index >> 4U); // no warning

sign_warn();
return 0;
}

int sign_warn(void)
{
unsigned int x=20, y=-30;
if (x > y) {
printf("%d > %d\n", x, y);
} else {
printf("%d <= %d\n", x, y);
}
return 0;
}
https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wclobbered-379

-Wconversion

Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.
 
Top