Signed mod unsigned

T

Tim Rentsch

Johannes Bauer said:
Wow -- I really don't feel bad about tripping over this now.
It *is* indeed confusion and without a *lot* of insight into
the standard it is quite hard to tell what the expression will
evaluate to on different platforms.

In practice it isn't hard to say what the result will be, because
the "pathological" implementations with such bizarre behavior are
very rare (indeed, I conjecture that none exist), and very likely
a developer would know if his platform had such issues. Granted,
the rule for converting the signed type to the unsigned type does
need to be known, but people who use mixed-mode arithmetic should
be familiar with that for other reasons (eg, comparisons).

When C was originally standardized, there was a big debate about
how to do integer promotions (in particular, should types like
'unsigned short' promote to signed int or unsigned int?). So
here's another case for you:

unsigned short us_30 = 30;
unsigned int ui_30 = 30;
int minus_one = -1;

// now, what is the result of

if( minus_one % us_30 == minus_one % ui_30 ) ... etc ...

Does the "etc" get executed or not? You might enjoy looking
into the Standard and see what you can make of this question.
 
T

Tim Rentsch

Ben Bacarisse said:
Johannes Bauer said:
Wow -- I really don't feel bad about tripping over this now. It *is*
indeed confusion and without a *lot* of insight into the standard it is
quite hard to tell what the expression will evaluate to on different
platforms.

I don't want to make you feel bad again, but this last case is one of
the oddest corners of C and can be quite safely ignored by almost
everyone (indeed by everyone, soon, if C11 gets a hold).

The main fact, that mixed int and unsigned int arithmetic is done by
converting the int operand to unsigned int, is commonplace and needs to
be kept in mind for those times when it will matter. The fact that it's
happening is often masked by implementations that use 2's complement and
define the unsigned to int conversion as simply "stuffing the bits back
in there". However

int x;
/* ... */
x = x + 1u;

can raise a signal when x is, say, -1 on systems that define the
conversion of out-of-range values to int as doing so. [snip]

Forgive me if this is a stupid question -- can you explain how
starting with x == -1 can give rise to anything other than x == 0
after the assignment? To me the behavior here looks well-defined.
 
T

Tim Rentsch

Ben Bacarisse said:
Tim Rentsch said:
Sort of. This curious behavior was not present in C90 or
in the original C99. It was added via a TC (it appears
in N1124) as a result of Defect Report 230. However,
subsequently it was acknowledged that the strange effect
on the promotion rules was an unintended consequence of a
poor wording choice to address the DR. This has since
been corrected in C11 -- under C11 unsigned int is never
'promoted' to int even if UINT_MAX == INT_MAX. Hence the
expression -1%30u can never be -1 (or any negative number)
under the current standard.

Well that's pleasing to know, although I've never had to
wrestle with such an implementation. As far as I can tell, if
it followed C99 to the letter, the only way to do unsigned
arithmetic would be to force one or more of the operands to be
"wider" than unsigned int. [snip]

Most likely there are no such implementations. Assuming
however that such hardware platforms still exist, implementors
determined to do a C99 implementation on them would be well
advised to have INT_MAX == UINT_MAX/2 + 1, by making the
high-order bit of a hardware integer value be a 'padding bit'
as far as the C implementation is concerned. Taking this
approach we could still get a full range of values for
int-only operations (taking advantage of "undefined behavior"
for values that have that bit set), but signed/unsigned
mixtures would not behave pathologically.

(Under the heading of 'note to self': I really should get in
the habit of putting

#include <limits.h>
#if UINT_MAX == INT_MAX
#error UINT_MAX == INT_MAX -- need I say more
#endif

in my common header files.)
 
B

Ben Bacarisse

Tim Rentsch said:
Ben Bacarisse <[email protected]> writes:
The main fact, that mixed int and unsigned int arithmetic is done by
converting the int operand to unsigned int, is commonplace and needs to
be kept in mind for those times when it will matter. The fact that it's
happening is often masked by implementations that use 2's complement and
define the unsigned to int conversion as simply "stuffing the bits back
in there". However

int x;
/* ... */
x = x + 1u;

can raise a signal when x is, say, -1 on systems that define the
conversion of out-of-range values to int as doing so. [snip]

Forgive me if this is a stupid question -- can you explain how
starting with x == -1 can give rise to anything other than x == 0
after the assignment? To me the behavior here looks well-defined.

Yes, I should have said "x = -2" or, indeed, any int value < 0 other
than the one I picked.
 
K

Keith Thompson

Tim Rentsch said:
However

int x;
/* ... */
x = x + 1u;

can raise a signal when x is, say, -1 on systems that define the
conversion of out-of-range values to int as doing so. [snip]

Forgive me if this is a stupid question -- can you explain how
starting with x == -1 can give rise to anything other than x == 0
after the assignment? To me the behavior here looks well-defined.

I was in the middle of explaining why it's not well-defined when
I realized that it actually is.

The operands of "+" are of types int and unsigned int. The "usual
arithmetic conversions" cause the left operand to be converted
from int to unsigned int. The int value -1 is outside the range of
the target type -- but int-to-unsigned conversion is well defined,
and yields UINT_MAX. UINT_MAX + 1u yields 0u, and the assignment
converts 0u (type unsigned int) to 0 (type int).
 
B

Ben Bacarisse

Keith Thompson said:
Tim Rentsch said:
However

int x;
/* ... */
x = x + 1u;

can raise a signal when x is, say, -1 on systems that define the
conversion of out-of-range values to int as doing so. [snip]

Forgive me if this is a stupid question -- can you explain how
starting with x == -1 can give rise to anything other than x == 0
after the assignment? To me the behavior here looks well-defined.

I was in the middle of explaining why it's not well-defined when
I realized that it actually is.

The operands of "+" are of types int and unsigned int. The "usual
arithmetic conversions" cause the left operand to be converted
from int to unsigned int. The int value -1 is outside the range of
the target type -- but int-to-unsigned conversion is well defined,
and yields UINT_MAX. UINT_MAX + 1u yields 0u, and the assignment
converts 0u (type unsigned int) to 0 (type int).

Yes. Originally I wrote it without an example value for x, but then I
thought "I'll give an example value to make it concrete" and I went and
chose the only negative value that invalidate the example!
 
J

Johannes Bauer

I don't want to make you feel bad again, but this last case is one of
the oddest corners of C and can be quite safely ignored by almost
everyone (indeed by everyone, soon, if C11 gets a hold).

No worries, you haven't made me feel bad again :)

Much rather curious: In what ways does C11 change this behavior? I
haven't looked into C11 at all yet and am really interested what that
change could be (while still remaining backwards compatible).

Best regards,
Joe

--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
B

Ben Bacarisse

Johannes Bauer said:
No worries, you haven't made me feel bad again :)

Much rather curious: In what ways does C11 change this behavior? I
haven't looked into C11 at all yet and am really interested what that
change could be (while still remaining backwards compatible).

What's happened (thanks, Tim, for making me go look) is that the phrase
"other than int or unsigned int" has been added to exclude them from the
integer promotions. This means that unsigned int is never promoted to
int, even on those odd systems where the rule would otherwise apply ("if
an int can represent all of the values of the original type [...] the
value is converted to an int").

This is not backwards compatible on those odd implementations, but since
it seems the effect of the old rule on unsigned int was introduced
inadvertently, the committee must have felt it worth breaking
compatibility to tidy this up.
 
K

Keith Thompson

Ben Bacarisse said:
Johannes Bauer said:
No worries, you haven't made me feel bad again :)

Much rather curious: In what ways does C11 change this behavior? I
haven't looked into C11 at all yet and am really interested what that
change could be (while still remaining backwards compatible).

What's happened (thanks, Tim, for making me go look) is that the phrase
"other than int or unsigned int" has been added to exclude them from the
integer promotions. This means that unsigned int is never promoted to
int, even on those odd systems where the rule would otherwise apply ("if
an int can represent all of the values of the original type [...] the
value is converted to an int").

This is not backwards compatible on those odd implementations, but since
it seems the effect of the old rule on unsigned int was introduced
inadvertently, the committee must have felt it worth breaking
compatibility to tidy this up.

It's not backwards compatible with implementations where an int can old
all the values of unsigned int that followed the letter of the standard.

I rather doubt that there are any such implementations in the real
world. My guess (and it's only a guess) is that there are very few
implementations with UINT_MAX == INT_MAX, and of those implementations
few if any fully conformed to C99.
 
T

Tim Rentsch

Ben Bacarisse said:
Johannes Bauer said:
No worries, you haven't made me feel bad again :)

Much rather curious: In what ways does C11 change this behavior? I
haven't looked into C11 at all yet and am really interested what that
change could be (while still remaining backwards compatible).

What's happened (thanks, Tim, for making me go look) is that the phrase
"other than int or unsigned int" has been added to exclude them from the
integer promotions. This means that unsigned int is never promoted to
int, even on those odd systems where the rule would otherwise apply ("if
an int can represent all of the values of the original type [...] the
value is converted to an int").

This is not backwards compatible on those odd implementations, but since
it seems the effect of the old rule on unsigned int was introduced
inadvertently, the committee must have felt it worth breaking
compatibility to tidy this up.

I think of this not as an incompatibility but just as correcting a
mistake that they (may have) thought no one implemented anyway.
Remember, this behavior was not in C99 originally; it was added
in a TC. For an implementation to be affected by it, it would
require: one, that the change in the TC be noticed; two, that
the implementor realize the consequences of the bad wording on
such promotions; and three, that the implementation be one of
those for which the bad wording was relevant. The likelihood of
all three of those occurring, especially given the relative lack
of attention given to C99 and the dearth of fulling conforming C99
compilers, seems low enough to make concerns about compatibility
be pretty much a non-issue.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top