#The details about type promotion

88 messages · Page 1 of 1 (latest)

echo plover
#

I read the C11 specs (last draft) and I think I don't really understand it, since I'm getting results that are not consistent with what I (thought I) read.

Here's the spec I referenced to https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

Say that there are two variables

signed int a = -5;
unsigned int b = -5;

If I were to compare the two, a would be promoted to unsigned int and the comparison would be done as unsigned int ?

What if instead I have

signed int a = -5;
unsigned long b = -5;

Would this be compared in unsigned long ? (I don't think it did. I check the assembly code generated with gcc and I could see that it loaded a as a signed integer, why is this the case ?)

Thank you !!

tender zealotBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

stiff stirrup
#

and easy way of testing this is to use an operator like + and then check the resulting type with _Generic

echo plover
#

I legit started reading the assembly to try to understand it

stiff stirrup
#

;compile

#include<stdio.h>
int a=0;
unsigned b=0;
puts(_Generic(a+b,int:"int",unsigned:"unsigned",default:"other"));
proven sierraBOT
#
Program Output
unsigned
echo plover
#

I thought if a is implicitly converted to unsigned long, it would be loaded as if it were a unsigned long ?

stiff stirrup
echo plover
#

Yeah I mean that's the sane choice lol

stiff stirrup
# echo plover Yeah I mean that's the sane choice lol

in any case the rules are pretty well defined: after integer promotions (not considering mixing floats and ints here)

  1. if both types have the same signedness (that is, both are signed or both are unsigned) then the resulting type is the type that has higher conversion rank;
  2. otherwise if the signed type is capable of representing all of the values of the unsigned type, the resulting type is the signed type;
  3. otherwise if the signed type has a higher conversion rank then the resulting type is the unsigned type corresponding to the signed type;
  4. otherwise the resulting type is the unsigned type.
#

the rules have some interesting consequences, such as unsigned long+long long often resulting in the type unsigned long long

echo plover
#

What about this ?
Why is the result signed int and not unsigned int ? (if I understand correctly, they are of the same rank according to the specs)

stiff stirrup
echo plover
#

ah ha I see

stiff stirrup
#

it is really weird tbh

echo plover
#

Give me a sec I'll try to digest the rules you just wrote

#

Thanks a lot

stiff stirrup
#

;compile -ftrapv

unsigned short u=-1;
printf("%hu\n",u*u*u*u);
proven sierraBOT
#
Compiler Output
Program terminated with signal: SIGSEGV
stiff stirrup
#

for example this program has UB because of signed integer overflow

#

;compile -ftrapv

unsigned u=-1;
printf("%u\n",u*u*u*u);
proven sierraBOT
#
Program Output
1
stiff stirrup
#

and this is completely fine

echo plover
#

But why would there be a promotion here ?

#

u is of the same type as itself ?

stiff stirrup
#

even with ?:

echo plover
#

whyyyyy

#

ok

stiff stirrup
#

just the history of the language mostly

echo plover
#

yeah I mean that's a good reason

stiff stirrup
#

it probably made more sense on machines like the PDP where all operations were done with words

echo plover
#

So the operations today are not done with words ?

#

(how big is a word)

stiff stirrup
#

one of the main things about C originally was having a separate byte type char and word type int

echo plover
#

hmmm

stiff stirrup
#

;asm -O3

void foo(unsigned char*a,const unsigned char*b){
    *a+=*b;
}
proven sierraBOT
#
Assembly Output
foo(unsigned char*, unsigned char const*):
  movzx eax, BYTE PTR [rsi]
  add BYTE PTR [rdi], al
  ret

stiff stirrup
echo plover
stiff stirrup
#

modern x86 has no problems with doing operations on 1 byte quanities

#

another case of promotion was floats being implicitly promoted to double

#

this was removed in standardization, except for this conversion happening with variadic functions and unprototyped functions

echo plover
#

hmmmm

stiff stirrup
#

for example %g works with both floats and doubles because floats promote to doubles when being passed

echo plover
#

Btw, is passing an array as an argument to a function and having it becoming a pointer also a kind of promotion ?

stiff stirrup
#

in general it's best to avoid mixing signed and unsigned types; though if you stay within bounds of the unsigned type strictly then it should be fine

echo plover
stiff stirrup
echo plover
stiff stirrup
#

Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal
used to initialize an array, an expression that has type "array of type" is converted to an expression
with type "pointer to type" that points to the initial element of the array object and is not an lvalue.
If the array object has register storage class, the behavior is undefined.
Section 6.3.2.1 "Lvalues, arrays, and function designators" Paragraph 3 C17

stiff stirrup
echo plover
stiff stirrup
echo plover
#

And the other one that crashes

echo plover
stiff stirrup
#

the rules for integer promotions are like so:

  1. if the type has a conversion rank that is great than or equal to int, then the promoted type is the type;
  2. otherwise, if int is capable of representing all of the value of the type, then the type is int;
  3. otherwise the type is unsigned int
echo plover
#

oh yeah ok

stiff stirrup
echo plover
#

If I were to rephrase it
if the type is smaller than int -> int
if the type can't fit in int -> unsigned int

stiff stirrup
#

for example if short has the same width as int

echo plover
stiff stirrup
#

it still promotes to int

stiff stirrup
echo plover
stiff stirrup
#

or mathematically, if U is a subset of T

echo plover
#

But like I mean, -5 can't be represented in an unsigned int, but that's not what "can represent" mean here I think ?

stiff stirrup
#

lets say unsigned short is 16 bits and int is 32 bits, with two's complement
therefore the range of representable values for these types will be
unsigned short: [0,65535]
int: [-2147483648,2147483647]
given these ranges unsigned shortint; that is, you can't find a value in the range of numbers represented by unsigned short which is not represented in the range of numbers represented by int

echo plover
#

Then I think there's another problematic point in my example, what does unsigned int a = -5; actually do ?

#

It sets it to a very high int value doesn't it

#

Wait I think I actually gets it now

stiff stirrup
echo plover
#

I don't really get what reduced mean here tbh

#

I think I'll leave this here

#

You already clearified a lot of this

#

Thank you !!!!!

tender zealotBOT
#

@echo plover Has your question been resolved? If so, type !solved :)

stiff stirrup
#

or in other words, the range is reduced

echo plover
#

!solved

tender zealotBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity