It's also a way to help with some calculations. For example,
you can start from a given date, subtract fourteen from tm_mday,
re-normalize, and easily find out "What date was a fortnight
before January 5?"
Exactly.
You can't do that with signed integers, either.
Of course, signed integers are not without their limits (and the design
limits should lie safely within the environmental limits).
However, *most* of the values we deal with are in small ranges that are
not too far from zero; ranges near INT_MAX, for example, but far from
zero are uncommon, if any, and they may need an upgrade to a larger
integer type anyway.
Given that, getting around in signed is usually safe, as you're near
the center, far from the edges, of the field. Doing that in unsigned,
however, is like playing near the edge and risks falling off.
,- usually here
*****
signed [-------+-------+-------+-------]XXXX DANGER
| | |
-INT_MAX 0 INT_MAX UINT_MAX
| | |
unsigned DANGER XXXX[-------+-------+-------+-------]
And in the cases where naive subtraction doesn't work, you don't
just get a possibly surprising but predictable outcome: You get
undefined behavior.
True. But (2U - 5U) yielding 4294967293U is not very useful, no matter
how predictable it is, and failing to take that into account is a bug,
just as getting undefined behavior from signed arithmetic overflow is.
Oh, come on! You might just as well complain about double-ness
being "contagious."
Their "contagiousnesses" per se may be similar, but their effects are
different; conversion from integer to double doesn't (usually) change
the value being converted, or the result of the comparison at least,
but conversion from signed to unsigned often does, often in a very
surprising way, which is exactly my point in the paragraph above.
Besides, why blame the surprises on the unsigned operand? They
don't arise from either one of the operands in isolation, but from
the combination of the two -- so the signed operand is every bit as
much to blame as the unsigned. If you blame one, you should blame
the other equally.[*]
[*] Okay, that doesn't always happen in real life: Doheny was
acquitted of offering the bribe that Fall was convicted of taking.
But Roaring Twenties jurisprudence is a poor model for programming!
Sorry, I don't understand the footnote.
Anyway, you're right that the surprises arise from the combination.
If your values are usually closer to the upper limit of signed (say,
INT_MAX) than to zero, then signed gets in the way more often and may
deserve more "blame." If, on the other hand, they are usually closer
to the lower limit of unsigned, i.e. zero, then unsigned more often
gets in the way. If both cases happen equally, you may want to blame
them equally. In most of the cases I encounter, they don't.
Your mileage may vary, though.
... or when mixing signed integer with long double complex, or
when mixing unsigned long with pointer-to-pointer-to-T, or ... In
fact, your recommendation to "be careful when..." can be improved
by deleting "when" and everything after it. Just be careful, okay?
It's not a unanimous improvement, as it makes my statement more general
and more "vacuously true." Maybe I should have said "be *more* careful"
to be clearer, but I won't delete the when-clause. I don't object to
your being careful in everything, though.