10

Why does the following code output y>x when clearly 1>-1?

unsigned x=1;
signed char y=-1;

if(x>y){
    printf("x>y");
}
else {
    printf("y>x");
}

Please explain this result.

DeveloperDon
  • 4,988
Sudhir
  • 217
  • 3
    (to the people who closed it) How does this not relate to software development ? I was surprised to learn of this behavior in C through this question and though I don't use C much I'm glad, that as a programmer, I've learned this now. – Newtopian Nov 26 '15 at 14:25

3 Answers3

11

Implicit type conversion is biting you. Because x is unsigned, y is cast to unsigned as well; because -1 doesn't fit in an unsigned char, it overflows and becomes 255 (the bit-wise unsigned char equivalent of -1), which obviously is larger than -1.

tdammers
  • 52,636
  • 15
  • 110
  • 155
  • 1
    But how Compiler decide whether unsigned would be cast to signed or vice-versa ? – Sudhir Nov 09 '12 at 11:08
  • sudhir: It will just do it because it has to. It just can't compare them otherwise at all. The internal bit representation is different and not converting would not make sense too. In fact there isn't really a "good" way to do it for the compiler and it's the job of the programmer to prevent such situations by either using proper datatypes or doing manual conversion when necessary. – thorsten müller Nov 09 '12 at 11:12
  • @thorstenmüller It means always there would be casting of signed to unsigned. Is there is a possibility of unsigned to signed conversion? – Sudhir Nov 09 '12 at 11:16
  • @sudhir: yes, of course. You can always cast explicitly to avoid such things. Another thing I've seen a lot is storing individual chars in int variables. – tdammers Nov 09 '12 at 11:18
  • @thorstenmüller I have one more confusion when Iam removing unsigned from int then the output must be same as by-default int is unsigned.But the output gets reversed.Why so? – Sudhir Nov 09 '12 at 11:27
  • @sudhir: see for example this question on SO, and more generally search for C integer promotion rules. Oh, and int is signed by default. – Useless Nov 09 '12 at 11:29
  • @Useless Sorry for saying "int is unsigned by default" – Sudhir Nov 09 '12 at 11:32
  • @sudhir: I don't have a C compiler available right now. But the internal bit representation is a tricky thing. Maybe this and this will help, escpecially the part about the "Ones' complement" will give you an idea about the limits. If any possible, only use identical datatypes and datatypes that can hold the full range of values you need, eg a char with 0..255 and -128 to 127 will give you trouble if you want to convert a unsigned value like 200 to signed. – thorsten müller Nov 09 '12 at 11:34
  • @thorstenmüller If I write char c;printf("%d", c); it would print always postive or a negative number? – Sudhir Nov 09 '12 at 11:38
  • @sudhir: %d defines a signed decimal integer. %u would force unsigned. But I'm not sure if it would convert at all in printf or simply use the bit pattern "as is" and print nonsense if it gets wrong data. Best bet: Just put in a few variables with "edge values" and see what comes out. In many cases it will not be what you would hope for! C really works on a very low memory level here. This has advantages, but has disadvantages too until you take the time to learn about those internals in detail. You must not only know about the syntax, but about the underlying architecture too. – thorsten müller Nov 09 '12 at 11:44
  • Also I would like to know what is by-default char in C:unsigned char or signed char? – Sudhir Nov 09 '12 at 11:45
  • @thorstenmüller: I thought "%d" would crash if you pass a char, but apparently, it implicit-casts to an appropriate integer type. – tdammers Nov 09 '12 at 11:47
  • @tdammers: Really some time back I used this. A lot could happen. Assume it just takes the bytes it finds. If neighbouring bytes are zeros by chance, it could look like an implicit conversion while in fact it does nothing. I would not exactly trust this until I read the source code or some documentation. There may even be differences between different implementations if there is not a defined standard. – thorsten müller Nov 09 '12 at 11:51
2

@tdammers answer is correct, let me expand a bit.

The binary representation for negative values assumes the highest bit has value of 1. E.g., all negative 8-bit values look like 1xxx xxxx.

8-bit value for -1 is 1111 1111.

Hence, the very same binary 1111 1111 for unsigned is 255. So, the same binary value can be interpreted two different ways, depending if it is signed or not.

In your case, as @tdammers noticed, your -1 is stored into signed variable, but then implicitly interpreted as unsigned. Of course, 1 < 255, hence the result.

P.S. You should always check compiler warnings. All modern compilers would raise a warning on this matter.

1

This is a case where hopefully you are getting a compiler warning about the mixing of a signed and unsigned value. It may be even more specific where it talks about an unsigned lvalue and a signed rvalue.

This example underscores the hazards of C and to a lesser extent, C++ (which tends to be a little more strict about type checking, and which offers multiple kinds of casts). If you want to write good quality C code several things can help:

  • As a basic precaution, use compiler options to generate the strictest warnings possible.
  • Very specifically, avoid mixing of signed and unsigned values in comparisons and most calculations. (signed = signed * unsigned OK, signed = signed + unsigned OK, most other mixing, bad).
  • Use printf(), trace(), or inspection of variables in a debugger to better understand where things go wrong. If your program included a statement like printf("x=%d, y=%d/n", x, y); you would see that things went bad as -1 was assigned into y.
  • Study, study, study... C is a demanding language and you should make yourself as expert as possible on the semantics and side-effects of signed and unsigned comparisons and calculations. You also need a very high degree of attention to detail about the range of input values to equations, the possible range of equation results, and whether constants can actually fit into the variables to which they are assigned.
  • When appropriate, use defensive programming techniques like asserts to expose programming problems during development.
Dan Pichelman
  • 13,813
DeveloperDon
  • 4,988
  • The compiler does not warn. Implicit casts, even narrowing ones, are valid C, and no warning is issued. Explicit casting on variable assignment is seldom required in C. – tdammers Nov 09 '12 at 11:48
  • To elaborate: gcc 4.7.1 at least does not warn, at least not for signed vs. unsigned char, and it does produce the correct result (i.e., signed -1 tests smaller than unsigned 1) - but it does warn for signed vs. unsigned int (and, consequently, produces the "wrong" result). My guess would be that gcc casts to a larger integer type to do the comparison; I'm unsure though whether this is standard C behavior. – tdammers Nov 09 '12 at 11:55