Author Topic: Comparing int and uint in C  (Read 5136 times)

0 Members and 3 Guests are viewing this topic.

Offline anso-engineerTopic starter

  • Regular Contributor
  • *
  • Posts: 104
  • Country: ua
Comparing int and uint in C
« on: September 21, 2026, 09:21:37 am »
Hello, recently I was asked about comparing signed and unsigned ints in C.
There was some tricky example which pushed me to explain why -10 is greater than 10.
 
And how efficiently compare int ant uint in general? Because I still haven't found universal variant.
 

Offline 5U4GB

  • Super Contributor
  • ***
  • Posts: 1750
  • Country: au
Re: Comparing int and uint in C
« Reply #1 on: September 21, 2026, 09:32:11 am »
You'd have to explicitly range-check around INT_MAX and 0 or whatever the underlying type is, so check if the signed value is < 0 -> always less then, unsigned value > INT_MAX -> always greater than.
 
The following users thanked this post: anso-engineer

Offline madires

  • Super Contributor
  • ***
  • Posts: 9196
  • Country: de
  • A qualified hobbyist ;)
Re: Comparing int and uint in C
« Reply #2 on: September 21, 2026, 10:00:53 am »
A good starting point is to understand the two's complement (https://en.wikipedia.org/wiki/Two%27s_complement).
 
The following users thanked this post: anso-engineer

Offline dmendesf

  • Frequent Contributor
  • **
  • Posts: 404
  • Country: br
Re: Comparing int and uint in C
« Reply #3 on: September 21, 2026, 11:34:02 am »
Let me abuse this topic for a much broader question: What's the autoritative source about C? (Meaning what's legal, illegal, implementation specific, undefined....). Is these such a thing for each revision of the language?
This is a serious question. 80% of my work is hardware design/implementation but software (embedded C)  keeps creeping in, and I like to work with well defined things.

Daniel
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Comparing int and uint in C
« Reply #4 on: September 21, 2026, 11:39:52 am »
On all modern computers (pretty much anything since 1970 I would say), -N is represented as 2^16-N, or 2^32-N or 2^64-N depending on the data type size.

The C standard says that if you compare a signed number to an unsigned number then the signed number is converted to unsigned first. You can't reason your way to this, it's arbitrary, you simply have to know it.


If you have int A and unsigned B and and you really want to do A < B using their mathematical values then you can do:

    if (a < 0 || a < b) ...
 
The following users thanked this post: hans, thm_w, Karel, 5U4GB

Offline madires

  • Super Contributor
  • ***
  • Posts: 9196
  • Country: de
  • A qualified hobbyist ;)
Re: Comparing int and uint in C
« Reply #5 on: September 21, 2026, 12:06:26 pm »
Let me abuse this topic for a much broader question: What's the autoritative source about C? (Meaning what's legal, illegal, implementation specific, undefined....). Is these such a thing for each revision of the language?

Please see https://en.wikipedia.org/wiki/ANSI_C.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3529
  • Country: ca
Re: Comparing int and uint in C
« Reply #6 on: September 21, 2026, 12:41:50 pm »
Because of the C integer promotion rules, it would work if you had types smaller than int:

Code: [Select]
#include <stdio.h>
#include <stdint.h>

uint16_t u16;
int16_t s16;

uint32_t u32;
int32_t s32;

int main() {
  u16 = 1;
  s16 = -1;
 
  u32 = 1;
  s32 = -1;
 
  printf("u32 is %s than s32\r\n",(u32 > s32)?"more":"less");
  printf("u16 is %s than s16\r\n",(u16 > s16)?"more":"less");
}

This would print:

Code: [Select]
u32 is less than s32
u16 is more than s16

Worse yet. On some platforms (PIC16 for example), int may be 16-bit so the result of comparison between u16 and s16 is platform-dependent.
 

Offline 5U4GB

  • Super Contributor
  • ***
  • Posts: 1750
  • Country: au
Re: Comparing int and uint in C
« Reply #7 on: September 21, 2026, 01:03:32 pm »
Let me abuse this topic for a much broader question: What's the autoritative source about C? (Meaning what's legal, illegal, implementation specific, undefined....). Is these such a thing for each revision of the language?

Please see https://en.wikipedia.org/wiki/ANSI_C.

That's the authoritative source in the same way that the Federal Register of Legislation is the authoritative source on Australian law.  Neither of them are useful sources to point someone to unless you happen to be engaged in an obsessive legal debate about the specifics of some obscure trivia.  The ISO C standard for example is 700 pages of stuff that's mostly incomprehensible to someone who hasn't spent half a lifetime on the C standards committee.
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 635
  • Country: be
Re: Comparing int and uint in C
« Reply #8 on: September 21, 2026, 02:27:48 pm »
Let me abuse this topic for a much broader question: What's the autoritative source about C?

ISO/IEC 9899 - Revision of the C standard :  https://www.open-std.org/jtc1/sc22/wg14/www/projects.html
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Comparing int and uint in C
« Reply #9 on: September 21, 2026, 03:13:23 pm »
Let me abuse this topic for a much broader question: What's the autoritative source about C?

ISO/IEC 9899 - Revision of the C standard :  https://www.open-std.org/jtc1/sc22/wg14/www/projects.html

You won't go far wrong if you refer to The C Programming Language 2nd Ed". There are plenty of PDFs on the internet e.g.

https://github.com/simonvar/c-language/
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17799
  • Country: fr
Re: Comparing int and uint in C
« Reply #10 on: September 21, 2026, 04:25:13 pm »
Let me abuse this topic for a much broader question: What's the autoritative source about C? (Meaning what's legal, illegal, implementation specific, undefined....). Is these such a thing for each revision of the language?
This is a serious question. 80% of my work is hardware design/implementation but software (embedded C)  keeps creeping in, and I like to work with well defined things.

What an odd question. The C standard is obviously the authoritative source. C has a standard that evolves on a regular basis, contrary to some other languages that have none.

The standards are "unfortunately" not free (as most standards, you have to pay for a copy), but the older ones are easily accessible to download and for later ones, you can easily grab (for free) final drafts of recent revisions of the standard, which are usually almost identical to the final version.

While the question is "odd" IMO, it's more unfortunate than odd - most C devs have never even opened the C standard in any revision and that's not a good thing. It's also a lot more accessible than, say, the C++ standard, which is thousands of pages long.

I wouldn't call the K&R book a very good source if you want to get to the bottom of those pesky questions, such as undefined behavior. It's a good introduction to C but can't replace reading the standard. It's also now pretty dated, many points on undefined behavior and implementation-defined stuff having been added in later revisions of the standard, and the K&R being stuck to "ANSI C", which is equivalent to C89. And again, even so, AFAIR, it doesn't get to the nitty gritty details of all subtleties of undefined behaviors and such. I don't really know of a book on C "better" than reading the standard for this. Books OTOH are good for learning purposes, learning C from scratch from the standard only would not make sense.
« Last Edit: September 21, 2026, 04:26:46 pm by SiliconWizard »
 
The following users thanked this post: westfw, newbrain

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 4842
  • Country: us
Re: Comparing int and uint in C
« Reply #11 on: September 21, 2026, 10:40:17 pm »
Quote
That's the authoritative source in the same way that the Federal Register of Legislation is the authoritative source on Australian law.  Neither of them are useful sources to point someone to unless you happen to be engaged in an obsessive legal debate about the specifics of some obscure trivia.  The ISO C standard for example is 700 pages of stuff that's mostly incomprehensible to someone who hasn't spent half a lifetime on the C standards committee.

It's still unarguably the authoritative source.

And I don't think it's incomprehensible.  It's just exhaustive.  So yes, if you are debating some very esoteric point, you may have to read 7 sections and cross reference them and try to infer exact meaning in some weird case.  But if you are just looking for the integer promotion rules, they are right there.


Here is the text of the binary operation promotion rules from C99:

Quote
Otherwise, the integer promotions are performed on both operands. Then the
following rules are applied to the promoted operands:

If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.

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.

Otherwise, 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, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.

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

Yes, this uses carefully constructed language and previously described definitions (the single value promotion and the promotion rank), but it's not that complicated.
 
The following users thanked this post: Siwastaja, newbrain

Online westfw

  • Super Contributor
  • ***
  • Posts: 4657
  • Country: us
Re: Comparing int and uint in C
« Reply #12 on: September 22, 2026, 05:27:15 am »
Quote
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.
Is it well defined exactly how a signed integer is converted to unsigned?
Ie: do we now assume 2's complement?  A hypothetical CPU using "excess 128" integer encoding could yield a different result for ui < -si
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11230
  • Country: fi
Re: Comparing int and uint in C
« Reply #13 on: September 22, 2026, 06:21:06 am »
Quote from: westfw link=topic=495680.msg6364084#msg6364084
Ie: do we now assume 2's complement?  A hypothetical CPU using "excess 128" integer encoding could

Yes, we do. C23 finally deprecated other encodings and mandated two's complement (other kind of hardware can still be made and support C of course, but the compiler needs to transparently act as if the hardware was 2's complement) - of course this was de facto state of matter for a long time already and only an issue to standard pedants.
 
The following users thanked this post: hans, Karel

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 6029
  • Country: gb
  • Doing electronics since the 1960s...
Re: Comparing int and uint in C
« Reply #14 on: September 22, 2026, 08:39:56 am »
Quote
if (a < 0 || a < b) ...

That's clever because if a < 0 then a must be < b if b is unsigned.

It is a horrible way to write code though, comparing a possibly negative number with an unsigned number. Should never do that in the first place.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4657
  • Country: us
Re: Comparing int and uint in C
« Reply #15 on: September 22, 2026, 09:40:28 am »
It happens because a lot of older code uses “int” for variables that are never “expected” to be negative (and should probably actually be something like size_t). (And then “sometimes” the old code uses -1 or similar to indicate an error)
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 6029
  • Country: gb
  • Doing electronics since the 1960s...
Re: Comparing int and uint in C
« Reply #16 on: September 22, 2026, 09:45:43 am »
But then the old code would not have an unsigned number to compare against, no?

If it used ints everywhere it would be ok.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 4842
  • Country: us
Re: Comparing int and uint in C
« Reply #17 on: September 22, 2026, 06:23:01 pm »
It happens because a lot of older code uses “int” for variables that are never “expected” to be negative (and should probably actually be something like size_t). (And then “sometimes” the old code uses -1 or similar to indicate an error)

It's not just older code.  The longer I use C and C++, the longer I think Java et al. are correct and using unsigned values for variables simply because "they should never be negative" is an anti-pattern.  Not just due to C promotion rules, but in general unsigned types are bad for normal arithmetic and should be avoided.

If you need range enforcement, write range checks.  Don't try to use a type which enforces only one side of the range and doesn't actually check anything at all.  It actually makes checks more complicated.

Back in the day, using unsigned types was a useful way to get twice the range of a limited width integer.  But it's very rare now that the one extra bit makes the difference, it's almost always better to bump up to the next size.

Unsigned types are great for bitwise arithmetic, if you actually want to operate on the integers mod 2^n, or if you are storing literal bit patterns like pointer values (which are just a special case of the integers mod 2^N).  I don't want to go as far as Java and exclude unsigned types altogether.  But I think if you use signed numbers for arithmetic and unsigned numbers for bitwise operations you will dramatically reduce the frequency you need to convert between them.  Then the cases you do can be explicitly cast and you see where you need to insert checks.

Of course this is only a guideline.  In practice you have to work with existing libraries and the language itself that use unsigned types for offsets and sizes.  I don't think any rule should keep you from using judgement when it's called for.
 
The following users thanked this post: Siwastaja

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3529
  • Country: ca
Re: Comparing int and uint in C
« Reply #18 on: September 22, 2026, 08:21:00 pm »
Back in the day, using unsigned types was a useful way to get twice the range of a limited width integer.  But it's very rare now that the one extra bit makes the difference, it's almost always better to bump up to the next size.

Free flowing timers/counters, head and tail in circular buffers, random number generators. These came to my mind right away, I'm sure there are many others.
 
The following users thanked this post: mikerj, Siwastaja, newbrain

Offline dan1138

  • Newbie
  • Posts: 4
  • Country: us
Re: Comparing int and uint in C
« Reply #19 on: September 22, 2026, 09:00:55 pm »
This topic has great insights in how the C language is used now. The language was designed at a time when computers would fill a room, they were used for calculations involving scientific and business application. Almost all if these required numbers that would range from negative to positive infinity.

In mathematics simple numbers are grouped in three sets: real, integer and ordinal.

Because the C language is targeted as a tool that can be used for a wide range of tasks, some methods are hostile to some tasks and supportive to others. The designers of C decided that developers are smart enough to understand these compromises. The language was crafted to efficiently compile statements at the cost of protection.

The unsigned data type in C is the set of ordinal numbers, they cannot represent the concept of a negative number. The signed data type can that is why it is the default for float and int storage classes.

Any statement that does a relative comparison of different data types has always been a source of bugs in the C language. The ISO language specification has tried to mitigate this without breaking too much legacy code. Opinions of how well vary.

An "official" K&R compiler will build this kind of statement without annoying diagnostics, trusting that developers know what they are doing. The modern compilers try to warn that something dumb is happening but when an explicit type cast of a signed to unsigned data type is used the developer is telling the compiler to "let me do the dumb".

Comparing unsigned to a signed data types is like comparing apples to pineapples. Usually pointless, often bitter.
« Last Edit: September 22, 2026, 09:18:18 pm by dan1138 »
 
The following users thanked this post: Geoff-AU

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17799
  • Country: fr
Re: Comparing int and uint in C
« Reply #20 on: September 22, 2026, 09:17:33 pm »
The root cause is that C is kind of weakly typed but at the same time is designed to be as efficient as possible. So the combination leads to these traps; although recent revisions of the standard have clarified things quite a bit.
Historically, C was derived from B, which itself, if I'm not mistaken, had only one scalar type: integer.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Comparing int and uint in C
« Reply #21 on: September 22, 2026, 10:58:49 pm »
Back in the day, using unsigned types was a useful way to get twice the range of a limited width integer.  But it's very rare now that the one extra bit makes the difference, it's almost always better to bump up to the next size.

Free flowing timers/counters, head and tail in circular buffers, random number generators. These came to my mind right away, I'm sure there are many others.

If you're talking about pointers to things and sizes of memory objects, most OSes reserve addresses with the hi bit set for the OS ...

As for counters, a 64 bit nanosecond counter won't wrap in your lifetime.

I do think use of unsigned for something that isn't effectively bit manipulation is some kind of disease.

Unfortunately a lot of 32 bit unsigned variables have in the last 25 years been needlessly "optimised" into codebases for things running on 64 bit CPUs that by default zero-extend 32 bit values to 64 bits (amd64, arm64), which is architecturally the lazy but wrong design choice. The newer RISC-V and LoongArch get it right here.
« Last Edit: September 22, 2026, 11:03:27 pm by brucehoult »
 

Offline Leiothrix

  • Regular Contributor
  • *
  • Posts: 137
  • Country: au
Re: Comparing int and uint in C
« Reply #22 on: September 22, 2026, 11:30:58 pm »
As for counters, a 64 bit nanosecond counter won't wrap in your lifetime.

Perhaps, but that doesn't help you on a 32, 16 or 8 bit platform.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Comparing int and uint in C
« Reply #23 on: September 22, 2026, 11:46:22 pm »
As for counters, a 64 bit nanosecond counter won't wrap in your lifetime.

Perhaps, but that doesn't help you on a 32, 16 or 8 bit platform.

Yes, but there it's not "unsigned" you want but MultiWordNum.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 4842
  • Country: us
Re: Comparing int and uint in C
« Reply #24 on: September 23, 2026, 02:46:55 am »
As for counters, a 64 bit nanosecond counter won't wrap in your lifetime.

Perhaps, but that doesn't help you on a 32, 16 or 8 bit platform.

Sure it does.  You can still use 64 bit types and the overhead isn't even that bad. 

But the main point is that if 31 bits is not enough, 32 is almost certainly not either.

On the other hand, timers and counters are exacly the sort of situations that cause problems with unsigned variables.  Because people inevitably want to subtract them.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->