Why, warning: comparison between signed and unsigned integer expressions?

L

Lox

Hello all experts. If I compile the following code (small example) I
get "warning: comparison between signed and unsigned integer
expressions".

void test(void)
{
uint8_t a;
uint32_t i;

a = 10;

for(i = 0; i < (a + 1); i++)
{

}
}

But the following code doesn't give that warning:


void test(void)
{
uint8_t a;
uint32_t i;

a = 10;

for(i = 0; i < (uint32_t)(a + 1); i++)
{

}
}

What is going on here? Why does the compiler think (a + 1) is signed?
 
I

Ike Naar

hi,

Hello all experts. If I compile the following code (small example) I
get "warning: comparison between signed and unsigned integer
expressions".

void test(void)
{
uint8_t a;
uint32_t i;

a = 10;

for(i = 0; i < (a + 1); i++)
{

}
}

what compiler are you using? i'm unable to reproduce this warning on
gcc-4.[567]

Use the -Wextra option.
 
I

Ike Naar

Hello all experts. If I compile the following code (small example) I
get "warning: comparison between signed and unsigned integer
expressions".

void test(void)
{
uint8_t a;
uint32_t i;

a = 10;

for(i = 0; i < (a + 1); i++)
{

}
}

Integer promotions.
In i < (a + 1), a is promoted to an int because, apparently on your
implementation, int can represent all values of type uint8_t.

The promoted a (int) and 1 (int) are added, yielding int (a + 1) .
The operands of the comparison are uint32_t and int, hence the warning.
But the following code doesn't give that warning:


void test(void)
{
uint8_t a;
uint32_t i;

a = 10;

for(i = 0; i < (uint32_t)(a + 1); i++)
{

}
}

Here the int expression (a + 1) is explicitly converted
to uint32_t, so the comparison is between two uint32_t's.
 
L

Lox

I get the same warning when i use:

void test(void)
{
uint8_t a;
uint32_t i;
a = 10;
for(i = 0; i < (a + UINT8_C(1)); i++)
{
}
}

and also on:

void test(void)
{
uint8_t a;
uint32_t i;
a = 10;
for(i = 0; i < ((uint8_t)a + (uint8_t)1); i++)
{
}
}

I'm using Ride7 IDE from Raisonance. It uses some form of GCC for ARM
(arm-none-eabi-gcc.exe).

But I think it is solved now when I understand that char and short
gets promoted to int. Thank you all.

hi,

On 05/29/2012 08:15 AM, Lox wrote:
what compiler are you using? i'm unable to reproduce this warning on
gcc-4.[567] and clang-3.0, but id say that a+1 is uint8_t+int, that
gives int, thus signed. try a+1u instead.
 
B

BartC

Ike Naar said:
Integer promotions.
In i < (a + 1), a is promoted to an int because, apparently on your
implementation, int can represent all values of type uint8_t.

So all the discussion in the recent thread ("condition true or false? ...")
about mixed arithmetic being unsigned, isn't always true?

(Yes I've now read 6.3.1.8. But why would C care more about preserving
possible negative values of a wider int, than the negative values of a
narrower int? The mixed arithmetic rule could have been more consistent.)
 
L

Lox

I also tested the integer promotion in the following examples.

uint32_t x;
uint8_t y;

x = 255;
y = x + UINT8_C(1);

y is 256 and not 255 as one would think.

but in:

uint32_t x;
uint8_t y;

x = 255;
y = (u8_t)(x + UINT8_C(1));

y is 255

:)
 
J

James Kuyper

On 05/29/2012 03:56 AM, Ike Naar wrote:
....
In i < (a + 1), a is promoted to an int because, apparently on your
implementation, int can represent all values of type uint8_t.

That's not specific to his implementation. uint8_t is an exact-sized
type, so UINT8_MAX has to be 255. An implementation where CHAR_BITS>8
cannot implement uint8_t, and therefore must NOT provide it. INT_MAX is
not allowed to be less than 32767, so uint8_t always promotes to int.
 
J

James Kuyper

So all the discussion in the recent thread ("condition true or false? ...")
about mixed arithmetic being unsigned, isn't always true?

A lot of things were said in that discussion by people with varying
levels of understanding of the language. What actually happens depends
upon the usual arithmetic conversions, which start with the integer
promotions. The assertion some participants made that "mixed arithmetic
is unsigned" was, at best, a simplification that becomes false under two
circumstances:

1. If all possible values of the unsigned type can be represented as an
'int', the unsigned value is promoted to an 'int'. The same rule applies
to the signed type. The expression is evaluated using whichever promoted
type has the higher integer conversion rank.

2. If the expression still has mixed signedness after the integer
promotions, the following rule applies to the promoted types of the
operands. If the unsigned type and has an integer conversion rank less
than that of the signed type, and all values of the unsigned type can be
represented in the signed type, then the expression is evaluated using
the signed type.
(Yes I've now read 6.3.1.8. But why would C care more about preserving
possible negative values of a wider int, than the negative values of a
narrower int? The mixed arithmetic rule could have been more consistent.)

One key concept in C is that 'int' and 'unsigned int' were chosen by the
implementation to be the natural type for ordinary integer arithmetic.
Therefore, C requires promotion to one of those two types, if the result
is value preserving, before anything other aspect of the usual
arithmetic conversions are applied. It is consistent application of that
rule that produces the result that you consider inconsistent.
 
I

Ike Naar

I also tested the integer promotion in the following examples.

uint32_t x;
uint8_t y;

x = 255;
y = x + UINT8_C(1);

y is 256 and not 255 as one would think.

Two questions:
- why would one think that y would be 255 ?
- uint8_t can hold the values [0,255], so how could y ever be 256 ?
but in:

uint32_t x;
uint8_t y;

x = 255;
y = (u8_t)(x + UINT8_C(1));

y is 255

Very unlikely. How did you come to that conclusion?
 
L

Lox

Sorry, two typos (wrote to fast). :p

uint32_t x;
uint8_t y;

x = 255;
y = x + UINT8_C(1);

y is 256 and not 0 as one would think (if assuming 8 bit
calculations).

but in:

uint32_t x;
uint8_t y;

x = 255;
y = (u8_t)(x + UINT8_C(1));

y is 0

:)
 
I

Ike Naar

Sorry, two typos (wrote to fast). :p

uint32_t x;
uint8_t y;

x = 255;
y = x + UINT8_C(1);

y is 256 and not 0 as one would think (if assuming 8 bit
calculations).

Again, y can only hold values from 0 to 255 inclusive,
so it cannot be 256. How did you come to that conclusion?
Can you show the code?

Here's code that suggests y equals 0:

#include <stdint.h>
#include <assert.h>
int main(void)
{
uint32_t x = 255;
uint8_t y = x + UINT8_C(1);
assert(y == 0);
return 0;
}
but in:

uint32_t x;
uint8_t y;

x = 255;
y = (u8_t)(x + UINT8_C(1));

What is (u8_t) ? Did you mean (uint8_t) ?

Indeed.
 
J

Joe Pfeiffer

Ike Naar said:
Again, y can only hold values from 0 to 255 inclusive,
so it cannot be 256. How did you come to that conclusion?
Can you show the code?

If I'm reading the standard correctly, an unsigned char is large
enough to hold values from 0 to 255 (actually "any member of the basic
execution character set"). It doesn't say that it can't be larger than
that.
Here's code that suggests y equals 0:

#include <stdint.h>
#include <assert.h>
int main(void)
{
uint32_t x = 255;
uint8_t y = x + UINT8_C(1);
assert(y == 0);
return 0;
}

I tried a similar experiment, and also got 0 (as you and I both
expected!). I wouldn't be at all surprised to learn that a processor
with a character set that didn't handle 8 bit quantities well, or indeed
some other compiler than gcc even on Intel, was able to store a value
greater than 255 in an unsigned char.
 
J

James Kuyper

If I'm reading the standard correctly, an unsigned char is large
enough to hold values from 0 to 255 (actually "any member of the basic
execution character set"). ...

Actually, the key requirement is that UCHAR_MAX >= 255 (5.2.4.2.1p1).
... It doesn't say that it can't be larger than
that.

While that's all perfectly true, it's not relevant. The type used in
this example was uint8_t, not char. uint8_t is required by the standard
to have exactly 8 values bits, no more, and no less; and no padding bits
(7.20.1.1p2). On any implementation where uint8_t can be implemented,
it's likely (but not required) to be the same type as unsigned char.
However, uint8_t is an optional type, for precisely the reason that it
cannot be implemented as specified on any implementation where CHAR_BIT > 8.
 
J

Joe Pfeiffer

James Kuyper said:
Actually, the key requirement is that UCHAR_MAX >= 255 (5.2.4.2.1p1).


While that's all perfectly true, it's not relevant. The type used in
this example was uint8_t, not char. uint8_t is required by the standard
to have exactly 8 values bits, no more, and no less; and no padding bits
(7.20.1.1p2). On any implementation where uint8_t can be implemented,
it's likely (but not required) to be the same type as unsigned char.
However, uint8_t is an optional type, for precisely the reason that it
cannot be implemented as specified on any implementation where CHAR_BIT > 8.

Ah. I was looking for the string uint8_t in the standard and not
finding it... You are correct.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top