Thread: Type Promotion

  1. #1
    Registered User
    Join Date
    Dec 2010
    Posts
    113

    Type Promotion

    I have read in the book, C: The Complete Reference, that hierarchy; (For C89)

    IF an operand is a long double
    THEN the second is converted to long double
    ELSE IF an operand is a double
    THEN the second is converted to double
    ELSE IF an operand is a float
    THEN the second is converted to float
    ELSE IF an operand is an unsigned long
    THEN the second is converted to unsigned long
    ELSE IF an operand is long
    THEN the second is converted to long
    ELSE IF an operand is unsigned int
    THEN the second is converted to unsigned int


    I have a question:

    What will happen, when I want to do this operation;

    a=3,000,000,000;
    b=-10;
    a+b;

    a is an unsigned long int constant in default and b is an int constant.

    According to the book, b must be conversed into unsigned long but it cannot be represented by an unsigned long.

    Then how will the type promotion occur?

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    You're wrong about -10 not being expressible in an unsigned long, though. It's a very large number. Then the result of a+b might be right in terms of math.

  3. #3
    Registered User
    Join Date
    Dec 2010
    Posts
    113
    Quote Originally Posted by whiteflags View Post
    You're wrong about -10 not being expressible in an unsigned long, though. It's a very large number. Then the result of a+b might be right in terms of math.

    Sorry I didn't know that negative numbers can be expressed in unsigned long. I am still not sure about it. Could you expand it a bit please? How can a negative number be represented in unsigned long? Which bit will store the sign?

  4. #4
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    It doesn't store the sign, it's just a very large number. In fact, it's actually 10 from ULONG_MAX. Then when you add, you might expect a very large number, but that number has to fit in 32 or 64 bits.

    Do this with a calculator:

    (3000000000+4294967286)%(2^32)

    = 2999999990

    Modular arithmetic explains how everything happens in this case. Sometimes, integer overflow can be erroneous (you wanted a number bigger than what you could express in 32 bits) but it works for problems like this.
    Last edited by whiteflags; 03-05-2011 at 02:49 PM.

  5. #5
    Registered User
    Join Date
    Dec 2010
    Posts
    113
    Firstly thanks a lot for your great answer.

    I looked a bit deeper.

    If b were a long int; the binary representation would be that:

    111111111 11111111 11111111 11110110

    And I think this one is equal to 4294967286 in unsigned long int.

    The right sum will have more than 32 bits but we cannot have the ones after the 32 bits. And the 32 bits we have give the result: 2999999990.

    I think I have understood the logic.

    Then another question: If I can express negative numbers in unsigned long, why will I need to use long int?

    Edit: Surely to have negative values in the result.

    I have a new question:

    Code:
    #include <stdio.h>
    
    int main(void)
    {
    long int y=-10;
    unsigned long int x=y;
    printf("%d",x);
    return 0;
    }
    This one gives me the result: -10
    Shouldn't it give 4294967286 as the result?
    Last edited by GokhanK; 03-05-2011 at 03:15 PM.

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    The sign for an unsigned integer in printf is different, it's actually %u.

    Since printf() actually works in run time, you have to not only pass it the right things but ask printf() to display them the right way. If your compiler warnings are on it should warn you about what you pass to printf() because it too checks the format string. But AFAIK they certainly don't have to.
    If I can express negative numbers in unsigned long, why will I need to use long int?
    Well, is that really the reason you want to use unsigned? Most of the time unsigned is there just for working with bits (the value doesn't matter) and not because you can express larger numbers with an unsigned value. Besides there are other promotions to consider.

    -2UL / 10.0

    Is that really -0.2? Nope.

  7. #7
    Registered User
    Join Date
    Dec 2010
    Posts
    113
    However I have another question now.

    Yes we can get the right answer for this one:

    a=3,000,000,000;
    b=-10;
    a+b;

    But what about that one:

    -2L*2UL*5.00 or just -2L*2UL


    111111111 11111111 11111111 11111110

    This one is the representation of -1 in long and 4294967294 in unsigned long.

    Then how will the compiler differ -2L*2UL from 4294967294*2UL after promotion.

    How will the promotions occur in these examples?

  8. #8
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Depends on what is the type of the variable that stores the result i.e. unsigned or signed long.

  9. #9
    Registered User
    Join Date
    Dec 2010
    Posts
    113
    Quote Originally Posted by itCbitC View Post
    Depends on what is the type of the variable that stores the result i.e. unsigned or signed long.
    Type promotion rules says: If one of them is long and the other one is unsigned long, the long one will be converted to unsigned. However it is not certain in the text I have read in an older post what will the type of the final result. But I think it will be unsigned long too.

    I still have some missing points about type conversation.

  10. #10
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Quote Originally Posted by GokhanK View Post
    Type promotion rules says: If one of them is long and the other one is unsigned long, the long one will be converted to unsigned. However it is not certain in the text I have read in an older post what will the type of the final result. But I think it will be unsigned long too.
    Nope! it depends entirely on how the result is stored / displayed.
    Run the below snippet of code and notice the difference in outputs.
    Code:
    printf("%ld\n", -2L * 2UL);  /* the result here displayed as signed long   */ 
    printf("%lu\n", -2L * 2UL);  /* the result here displayed as unsigned long */
    What you're talking about comes in play only when doing a comparison, as in
    Code:
    if (-2L * 2UL < 0)
        printf("result is signed\n");
    else
        printf("result is unsigned\n");
    Last edited by itCbitC; 03-15-2011 at 02:20 PM. Reason: typos

  11. #11
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Quote Originally Posted by itCbitC View Post
    Quote Originally Posted by GokhanK
    Type promotion rules says: If one of them is long and the other one is unsigned long, the long one will be converted to unsigned. However it is not certain in the text I have read in an older post what will the type of the final result. But I think it will be unsigned long too.
    Nope! it depends entirely on how the result is stored / displayed.
    Run the below snippet of code and notice the difference in outputs.
    Code:
    printf("%ld\n", -2L * 2UL);  /* the result here displayed as signed long   */ 
    printf("%lu\n", -2L * 2UL);  /* the result here displayed as unsigned long */
    What you're talking about comes in play only when doing a comparison, as in
    Code:
    if (-2L * 2UL < 0)
        printf("result is signed\n");
    else
        printf("result is unsigned\n");
    GokhanK's interpretation is strictly correct. According to the usual arithmetic conversions,
    Quote Originally Posted by C99 6.3.1.8
    Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result.
    [...]
    Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
    long and unsigned long have the same rank; therefore -2L is converted to unsigned long, and the multiplication is performed. The result unequivocally has type unsigned long.

    I'm pretty sure that passing an unsigned long to %ld is undefined behavior:
    Quote Originally Posted by C99 7.19.6.1
    If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
    I see no exception for allowing signed/unsigned variants of a type to be used interchangeably. Of course, almost all systems will allow you to do this, but the fact remains that an unsigned long is being passed whether or not %ld or %lu is specified. Intel's C Compiler can be picky about this:
    Code:
    t.c(6): remark #181: argument is incompatible with corresponding format string conversion
        printf("%ld\n", 5UL);
    Obviously a compiler does not define the language, but Intel apparently reads the standard the same way I do (no diagnostic is required, so the lack of a diagnostic in another compiler is not strong evidence).

  12. #12
    Registered User
    Join Date
    Dec 2010
    Posts
    113
    @cas

    Then do you have a comment about how to convert -2L to unsigned long?

    -2L*2UL=-4UL

    However, -4 is signed? Then how do we have the right result in a compiler?

  13. #13
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Umm...since you're using constants, you could just do:
    -2L * 2L = -4L
    If you need to deal with variables, cast explicitly:
    foo_is_a_long * (long) bar_is_an_unsigned_long

    There's no way to avoid the loss of precision when you make one bit the sign bit, so if bar is more than 2^31 - 1, you'll run into issues, or you need to increase to long long.

  14. #14
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Quote Originally Posted by GokhanK View Post
    @cas

    Then do you have a comment about how to convert -2L to unsigned long?

    -2L*2UL=-4UL

    However, -4 is signed? Then how do we have the right result in a compiler?
    -4 is signed, -4UL is not.

    -2UL is the same thing (assume a 32-bit system) as 0xfffffffe. This is being multiplied by 2. So the result would be 0x1fffffffc. The way unsigned arithmetic in C works means you just take the bottom 32 bits of this value to get the proper result, which means 0xfffffffc. This is the same as -4 converted to an unsigned long.

    Unsigned types wrap around, so -1 is the largest possible value, -2 is the next largest, and so on. -4UL is like saying (ULONG_MAX - 3).

  15. #15
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Then how will the compiler differ -2L*2UL from 4294967294*2UL after promotion.
    GohanK, hopefully it's become clearer for you since other people have joined the conversation, but the problem is that you seem to think these problems are different. They aren't.

    -2UL * 2 = -4UL

    This could be the right answer: -4 is simply expressed as an unsigned long.

    Or you could have meant to multiply

    4 294 967 294 * 2 = 8 589 934 588

    But that doesn't fit into a 32 bit integer. As I said, integer overflow can sometimes be erroneous. It depends on what you mean. Nothing about the promotion rules changes, here. It doesn't work to your intent.

    This problem is why you aren't really supposed to do arithmetic on unsigned values just because the range of possible values is larger. When a variable overflows, it's supposed to be an error. So if you want to do big number arithmetic, use something like gmp.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Undefined reference to.. probably Makefile problem
    By mravenca in forum C Programming
    Replies: 11
    Last Post: 10-20-2010, 04:29 AM
  2. How to pass a matrix/array from main to a function
    By Inukami in forum C Programming
    Replies: 7
    Last Post: 12-09-2009, 09:03 PM
  3. Replies: 0
    Last Post: 03-20-2008, 07:59 AM
  4. Errors
    By Rhidian in forum C Programming
    Replies: 10
    Last Post: 04-04-2005, 12:22 PM
  5. gcc problem
    By bjdea1 in forum Linux Programming
    Replies: 13
    Last Post: 04-29-2002, 06:51 PM