xdavidliu
xdavidliu

Reputation: 3052

inconsistent equality of signed and unsigned after overflow in C

I'm trying to test equality of signed and unsigned versions of short, int, and long in C. In particular, I have used the following test code:

#include <stdio.h>

int main() {
    signed short ss = 0x8000; // 2^15
    unsigned short us = 0x8000;
    printf("ss = %i, us = %i\n", ss, us);
    if (ss == us) { printf("ss == us\n"); }
    else { printf("ss != us\n"); }

    signed int si = 0x80000000; // 2^31
    unsigned int ui = 0x80000000;
    printf("si = %i, ui = %i\n", si, ui);
    if (si == ui) { printf("si == ui\n"); }
    else { printf("si != ui\n"); }

    signed long sl = 0x8000000000000000L; // 2^63
    unsigned long ul = 0x8000000000000000UL; // 2^63
    printf("sl = %li, ul = %lu\n", sl, ul);
    if (sl == ul) { printf("si == ui\n"); }
    else { printf("sl != ul\n"); }
}

The code outputs the following:

ss = -32768, us = 32768
ss != us
si = -2147483648, ui = -2147483648
si == ui
sl = -9223372036854775808, ul = 9223372036854775808
si == ui

So for shorts they are not equal, but for the other two they are. Is there something wrong with what I am assuming, or is this just a known quirk of C?

Upvotes: 0

Views: 687

Answers (2)

Doug Currie
Doug Currie

Reputation: 41220

It appears that in your compiler, both short and unsigned short can be converted to int without loss of information; their comparison is done after the conversion ("promotion").

The promotions are explained in the section Usual Arithmetic Conversions at securecoding.cert.org

  1. If both operands have the same type, no further conversion is needed.

  2. If both operands are of the same integer type (signed or unsigned), the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

  3. If the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type is converted to the type of the operand with unsigned integer type.

  4. If the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type is converted to the type of the operand with signed integer type.

  5. Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

Upvotes: 2

chqrlie
chqrlie

Reputation: 145297

Given your output, the short and unsigned short are smaller than int. Both get converted to int for the first comparison and the converted values differ.

For int and unsigned int, the comparison is performed as unsigned int. The conversion gives the same value and the comparison is true. Note that you should print the numbers with an appropriate format: printf("si = %i, ui = %u\n", si, ui);

For the long and unsigned long test, your computer seems to have 64 bit longs, the comparison is performed as unsigned long, the long value is converted to an unsigned long but has the same representation, to comparison is true again.

Note that you should not store numbers larger than the maximum value into a signed type:

signed short ss = 0x8000; is not guaranteed to store -32768 to ss.

Same problem for signed int si = 0x80000000; and signed long sl = 0x8000000000000000L;.

Upvotes: 0

Related Questions