4

Recently I heard signed/unsigned comparisons maybe tricky in C. e.g., Signed/unsigned comparisons and also some other questions.

My question is - if we have to compare singed type with unsigned (e.g., including =,>,< operators), what strategies exist to avoid problems that arise from such comparison?

Or we should make sure we always compare only int with int and unsigned with unsigned?

PS. Also it would be nice to know when such comparisons are dangerous?

Community
  • 1
  • 1
  • 1
    First: Can you think of an example where it really makes sense to compare signed-unsigned instead of signed-signed or unsigned-unsigned? Maybe it´s because of my coding "style", but currently, i can´t think of something – deviantfan Apr 03 '14 at 18:50
  • @deviantfan: Actually, this makes sense I was also thinking about smth like that. Imagine, if number is negative, then surely the unsigned number is larger. If it is positive - then they can be compared as unsigned values right? –  Apr 03 '14 at 18:51
  • If you can have values between the negative (signed) minimal value and the unsigned max. positive one, you have more values in your range than one single int of that byte size can hold (like 2^32 or 2^64 different numbers). That alone is enough to avoid it. Use bigger variables in that case. – deviantfan Apr 03 '14 at 18:53
  • @deviantfan: `unsigned char*` vs `char*`. I have ran into a situation once where `strcmp` failed and I couldn't figure out why. Took me a while to find it in this old code many years ago. – Engineer2021 Apr 03 '14 at 18:55
  • @staticx: The code to this would be very interesting, if you could find it easily. – deviantfan Apr 03 '14 at 18:57
  • @deviantfan: I don't have access to it anymore, but `strcmp` assumes the `chars` are `unsigned` so that if you pass in a `char*`, then the comparison fails. Turned out the fix was to use `unsigned char*` everywhere most of the time. I was dealing with regular ASCII which fits into an `unsigned char*` at the time. Poor programming practices on behalf of a former programmer led to a lot of bugs I found in 5 years there :) – Engineer2021 Apr 03 '14 at 19:07
  • @staticx: well if it requires unsigned char* it means you should not pass a char* to it right? –  Apr 03 '14 at 19:09
  • @dmcr_code: You can, but it may fail. It depends. – Engineer2021 Apr 03 '14 at 19:11
  • @staticx: I mean in general it is not a good idea to pass a different type of variable to a function - other than the type it expects :) –  Apr 03 '14 at 19:12
  • @dmcr_code: Of course, but in my case it was mangled between the types it took time to track it down. – Engineer2021 Apr 03 '14 at 19:13

1 Answers1

2

It's best to make sure your types match in advance. But if you can't:

If you know that the int will definitely not hold a negative value at that point, cast it to unsigned.

If you know that the unsigned value will be less than INT_MAX, cast it to int.

If neither of these hold, cast both values to a type that's large enough to hold all possible values you need to be able to handle. Another possibility (when there is no large enough type) is to use two comparisons: First compare the int with 0, and if it's non-negative, cast it to unsigned and compare to the unsigned value.

If you don't cast anything, the int will be cast to unsigned, but it's best to be explicit about this if it's what you want to do.

Comparing int with unsigned will not work correctly when the int has a negative value. This is because it will be implicitly cast to unsigned and this will change its value.

interjay
  • 107,303
  • 21
  • 270
  • 254
  • @interhay: I am thinking along these lines: "Another possibility (when there is no large enough type) is to use two comparisons: First compare the int with 0, and if it's non-negative, cast it to unsigned and compare to the unsigned value." -- In this case, the cast is pretty safe right? The unsigned will surely be larger than normal int. And if int is negative, surely the unsigned value is larger right? –  Apr 03 '14 at 18:56
  • Also to address other points. >>"If you know that the int will definitely not hold a negative value at that point, cast it to unsigned." How will this solve the issue? >>"If you know that the unsigned value will be less than INT_MAX, cast it to int." Ok, this makes sense - because it is sort of safe cast right? due to checking the range –  Apr 03 '14 at 18:59
  • What I mean is, instead of `i < u`, you can write: `i < 0 || (unsigned)i < u`. This works because the cast is only done if `i` has a value that fits in `unsigned`. – interjay Apr 03 '14 at 19:01
  • "This works because the cast is only done if i has a value that fits in unsigned" - which is any value of i, in case i is positive right? (assuming i and u have same length) –  Apr 03 '14 at 19:05
  • are there any issues with == also?; it would be nice if you can sum up some points in your answer –  Apr 03 '14 at 19:07
  • Yes, any non-negative `int` fits in `unsigned`. And the same issue occurs with `==`. – interjay Apr 03 '14 at 19:10
  • This formula "i < 0 || (unsigned)i < u" works similarly if I want to compare "i <= u" right? (and I guess i and u must have similar length) –  Apr 03 '14 at 19:17
  • @dmcr_code That should be `i < 0 || (unsigned)i <= u`. – interjay Apr 03 '14 at 19:21
  • Yes - and am I right about length of u and i, or it doesn't matter? –  Apr 03 '14 at 19:23
  • @dmcr_code There may or may not be problems with types of different sizes, it depends on the types and the implementation. The exact rules are explained in the question you linked. By the way, I don't think I've ever actually needed to write something like `i < 0 || (unsigned)i < u`, and you probably don't need it either - the other solutions I listed should usually be sufficient. I have to go now so won't be available to answer questions for a few hours. – interjay Apr 03 '14 at 19:30
  • ok thanks anyway. ps. just not sure how this solves the issue/works: "If you know that the int will definitely not hold a negative value at that point, cast it to unsigned.". But thanks anyway –  Apr 03 '14 at 19:36