does 0<(unsigned short)0x8000 hold?

Discussion in 'C Programming' started by Francois Grieu, Mar 27, 2012.

  1. I just tested my code for portability problems on systems
    where int is 16-bit, and found one such system where

    #include <stdio.h>
    int main(void)
    {
    printf("%d\n", 0<(unsigned short)0x8000 );
    return 0;
    }

    outputs 0. If I change 0< to 0u<, the output is 1.

    Is that a bug in this compiler, w.r.t. C99?


    As an aside, 6.5.8p2 puzzles me; it reads:
    "One of the following shall hold:
    — both operands have real type;
    — both operands are pointers to qualified or unqualified versions
    of compatible object types; or
    — both operands are pointers to qualified or unqualified versions
    of compatible incomplete types.
    (..)"

    Would my example imply conversion to *real*, at least conceptually ?!

    As an aside of the aside, I do not understand the third option in
    the constraint, and it has been removed in N1570.

    TIA,

    Francois Grieu
    Francois Grieu, Mar 27, 2012
    #1
    1. Advertising

  2. On Mar 27, 4:32 pm, Francois Grieu <> wrote:
    > I just tested my code for portability problems on systems
    > where int is 16-bit, and found one such system where
    >
    > #include <stdio.h>
    > int main(void)
    >         {
    >         printf("%d\n", 0<(unsigned short)0x8000 );
    >         return 0;
    >         }
    >
    > outputs 0. If I change 0< to 0u<, the output is 1.
    >
    > Is that a bug in this compiler, w.r.t. C99?


    unsigned short promotes to int if all values can be represented in
    int, otherwise it promotes to unsigned int (6.3.1.1p2), so yes, that
    looks like a bug.

    > As an aside, 6.5.8p2 puzzles me; it reads:
    > "One of the following shall hold:
    >  — both operands have real type;
    >  — both operands are pointers to qualified or unqualified versions
    >    of compatible object types; or
    >  — both operands are pointers to qualified or unqualified versions
    >    of compatible incomplete types.
    > (..)"
    >
    > Would my example imply conversion to *real*, at least conceptually ?!


    Integer types and floating point types are together called real types
    (6.2.5p17); yes, there is an implicit conversion to a real type, and
    that type is unsigned int.

    > As an aside of the aside, I do not understand the third option in
    > the constraint, and it has been removed in N1570.


    It seems to allow comparisons between two void * values, or between
    two int(*)[] values. It seems silly to allow that but not also allow a
    comparison between f.e. a void * and a char *, but I'm not sure why it
    was removed entirely.
    Harald van Dijk, Mar 27, 2012
    #2
    1. Advertising

  3. Francois Grieu

    James Kuyper Guest

    On 03/27/2012 10:32 AM, Francois Grieu wrote:
    > I just tested my code for portability problems on systems
    > where int is 16-bit, and found one such system where
    >
    > #include <stdio.h>
    > int main(void)
    > {
    > printf("%d\n", 0<(unsigned short)0x8000 );
    > return 0;
    > }
    >
    > outputs 0. If I change 0< to 0u<, the output is 1.
    >
    > Is that a bug in this compiler, w.r.t. C99?


    INT_MAX is allowed to be as low as 0x7FFF, and that's presumably true on
    a 16-bit system (though there's always been a lot of debate about what
    "N-bit system" really means). If so, conversion of (unsigned
    short)0x8000 to int would produce a negative value unless int has a
    sign-and-magnitude type. But if INT_MAX is 0x7FFF, then (unsigned
    char)0x8000 must be promoted to "unsigned int", rather than "int"
    (6.3.1.1p2), and therefore 0 must also be converted to unsigned int,
    before evaluating the expression. As a result that expression would be
    equivalent to 0U < 0x8000U, which is still guaranteed to have a value of
    1. I'd say this is a bug in the compiler.

    > As an aside, 6.5.8p2 puzzles me; it reads:
    > "One of the following shall hold:
    > — both operands have real type;
    > — both operands are pointers to qualified or unqualified versions
    > of compatible object types; or
    > — both operands are pointers to qualified or unqualified versions
    > of compatible incomplete types.
    > (..)"
    >
    > Would my example imply conversion to *real*, at least conceptually ?!


    "The integer and real floating types are collectively called
    real types." (6.2.5p17), therefore no conversion to a real type is
    required. As a result, the usual arithmetic conversions require that the
    integer promotions be applied to both operands (6.3.1.8p1).

    > As an aside of the aside, I do not understand the third option in
    > the constraint, ...


    You're referring to "both operands [of a relational operator] are
    pointers to qualified or unqualified versions of compatible incomplete
    types."? I think the trickiest concept used in that sentence is
    "pointers to ... compatible incomplete types". Is that what you're
    having trouble with? Each of the following declarations defines a
    pointer to an incomplete type:

    int (*p_incomplete_array)[];
    struct flexible_array
    {
    size_t count;
    int member[];
    } *pfa;
    // No definition provided for struct my_struct
    struct my_struct *pms;
    void *pv;

    > ... and it has been removed in N1570.


    I can confirm that it has been removed. The implication would seem to be
    that it's no longer possible to compare pointers to incomplete types for
    relative order. I find this surprising. Am I misunderstanding it? If not
    was it deliberately removed, or an accident? If deliberate, what was the
    rationale for doing so?
    James Kuyper, Mar 27, 2012
    #3
  4. Francois Grieu <> writes:

    > I just tested my code for portability problems on systems
    > where int is 16-bit, and found one such system where
    >
    > #include <stdio.h>
    > int main(void)
    > {
    > printf("%d\n", 0<(unsigned short)0x8000 );
    > return 0;
    > }
    >
    > outputs 0. If I change 0< to 0u<, the output is 1.
    >
    > Is that a bug in this compiler, w.r.t. C99?


    It looks wrong to me. GCC says 1 on my machine and that's what I'd
    expect.

    > As an aside, 6.5.8p2 puzzles me; it reads:
    > "One of the following shall hold:
    > — both operands have real type;
    > — both operands are pointers to qualified or unqualified versions
    > of compatible object types; or
    > — both operands are pointers to qualified or unqualified versions
    > of compatible incomplete types.
    > (..)"
    >
    > Would my example imply conversion to *real*, at least conceptually ?!


    Depends on what "conceptually" means here! 6.2.5 p 17 says:

    The type char, the signed and unsigned integer types, and the
    enumerated types are collectively called integer types. The integer
    and real floating types are collectively called real types.

    so it's really just a catch-all term like arithmetic type.

    > As an aside of the aside, I do not understand the third option in
    > the constraint, and it has been removed in N1570.


    It makes sense to remove it. You can compare two pointers to incomplete
    types for equality, but <, >, etc are defined in terms of sub-objects in
    a array, so only pointers to object type make good sense.

    Some previously well-defined code (albeit not very useful code) will now
    require a diagnostic.

    --
    Ben.
    Ben Bacarisse, Mar 27, 2012
    #4
  5. On 27/03/2012 18:15, James Kuyper wrote:
    > On 03/27/2012 10:32 AM, Francois Grieu wrote:
    >> I just tested my code for portability problems on systems
    >> where int is 16-bit, and found one such system where
    >>
    >> #include <stdio.h>
    >> int main(void)
    >> {
    >> printf("%d\n", 0<(unsigned short)0x8000 );
    >> return 0;
    >> }
    >>
    >> outputs 0. If I change 0< to 0u<, the output is 1.
    >>
    >> Is that a bug in this compiler, w.r.t. C99?

    >
    > (..) that expression would be equivalent to 0U < 0x8000U (..)


    I get to that also. It is a bug in the compiler. Main question solved.


    >> As an aside, 6.5.8p2 puzzles me; it reads:
    >> "One of the following shall hold:
    >> — both operands have real type;
    >> — both operands are pointers to qualified or unqualified versions
    >> of compatible object types; or
    >> — both operands are pointers to qualified or unqualified versions
    >> of compatible incomplete types.
    >> (..)"
    >>
    >> Would my example imply conversion to *real*, at least conceptually ?!

    >
    > "The integer and real floating types are collectively called
    > real types." (6.2.5p17),


    Ah! First aside solved. I read "real" and understood "floating".

    >> As an aside of the aside, I do not understand the third option in
    >> the constraint, ...


    > (..) I think the trickiest concept used in that sentence is
    > "pointers to ... compatible incomplete types". Is that what you're
    > having trouble with?


    Yes.

    > Each of the following declarations defines a
    > pointer to an incomplete type:
    >
    > int (*p_incomplete_array)[];
    > struct flexible_array
    > {
    > size_t count;
    > int member[];
    > } *pfa;
    > // No definition provided for struct my_struct
    > struct my_struct *pms;
    > void *pv;


    Ah, thanks for the illustration! Now I see 6.2.5p1, defining
    "incomplete type" as one of "types that describe objects but lack
    information needed to determine their sizes".
    So when I'm using a pointer to void, I'm using an "incomplete type",
    like Mr. Jourdain is using prose [I choose to never use the other
    forms, for portability].

    >> ... and it has been removed in N1570.

    >
    > I can confirm that it has been removed. The implication would seem to be
    > that it's no longer possible to compare pointers to incomplete types for
    > relative order. I find this surprising. Am I misunderstanding it? If not
    > was it deliberately removed, or an accident? If deliberate, what was the
    > rationale for doing so?


    Indeed, comparing pointers to void referring to elements in the same
    struct or array seems like a feature that could help on occasions, was
    OK in C99, and seems gone from C2011 for no apparent reason. I second
    the question.

    Francois Grieu
    Francois Grieu, Mar 27, 2012
    #5
  6. Francois Grieu

    Tim Rentsch Guest

    Francois Grieu <> writes:

    > I just tested my code for portability problems on systems
    > where int is 16-bit, and found one such system where
    >
    > #include <stdio.h>
    > int main(void)
    > {
    > printf("%d\n", 0<(unsigned short)0x8000 );
    > return 0;
    > }
    >
    > outputs 0. If I change 0< to 0u<, the output is 1.
    >
    > Is that a bug in this compiler, w.r.t. C99?


    Yes. There is a simple argument that it is:

    1. The value 0x8000 is always representable as an unsigned short.

    2. The rules for 'integer promotions' always preserve value.

    3. The rules for 'usual arithmetic conversions' preserve value
    for all operand values that are non-negative. (A negative
    operand value might become non-negative due to UAC, but
    all non-negative values don't change under UAC.)

    Since 0 < 0x8000 must hold, 0 < (unsigned short) 0x8000 must hold,
    because ultimately the same mathematical comparison is done.


    > As an aside, 6.5.8p2 puzzles me; [snip]
    >
    > As an aside of the aside, I do not understand the third option in
    > the constraint, and it has been removed in N1570.


    The removal is not a change but just indicative of a change in
    nomenclature. In C99, there are incomplete types and object
    types. In N1570, there are incomplete object types and complete
    object types, which together make up object types. So in N1570,
    both complete and incomplete types are included under the now
    more general 'object type' term, and hence the third option in
    C99 is folded into the second option in N1570.
    Tim Rentsch, Mar 27, 2012
    #6
  7. Ben Bacarisse <> writes:

    > Francois Grieu <> writes:

    <snip>
    >> As an aside of the aside, I do not understand the third option in
    >> the constraint, and it has been removed in N1570.

    >
    > It makes sense to remove it.


    No it didn't! This part of my reply was nonsense. It's perfectly
    possible to well-define relative comparisons between pointers to
    incomplete types.

    It's good to see that the change is just due to a change in terminology.

    --
    Ben.
    Ben Bacarisse, Mar 27, 2012
    #7
  8. Francois Grieu

    James Kuyper Guest

    On 03/27/2012 03:12 PM, Ben Bacarisse wrote:
    > Ben Bacarisse <> writes:
    >
    >> Francois Grieu <> writes:

    > <snip>
    >>> As an aside of the aside, I do not understand the third option in
    >>> the constraint, and it has been removed in N1570.

    >>
    >> It makes sense to remove it.

    >
    > No it didn't! This part of my reply was nonsense. It's perfectly
    > possible to well-define relative comparisons between pointers to
    > incomplete types.
    >
    > It's good to see that the change is just due to a change in terminology.


    Could you explain the change in terminology? I thought about the
    possibility that something like that was involved, but I didn't notice
    anything relevant.
    James Kuyper, Mar 27, 2012
    #8
  9. On Mar 27, 9:32 pm, James Kuyper <> wrote:
    > On 03/27/2012 03:12 PM, Ben Bacarisse wrote:
    > > It's good to see that the change is just due to a change in terminology..

    >
    > Could you explain the change in terminology? I thought about the
    > possibility that something like that was involved, but I didn't notice
    > anything relevant.


    I didn't notice it either, but as Tim Rentsch pointed out, the change
    is in 6.2.5p1.

    Old: "Types are partitioned into object types (types that fully
    describe objects), function types (types that describe functions), and
    incomplete types (types that describe objects but lack information
    needed to determine their sizes)."

    New: "Types are partitioned into object types (types that describe
    objects) and function types (types that describe functions)."
    Harald van Dijk, Mar 27, 2012
    #9
  10. Francois Grieu

    James Kuyper Guest

    On 03/27/2012 03:47 PM, Harald van Dijk wrote:
    > On Mar 27, 9:32 pm, James Kuyper <> wrote:
    >> On 03/27/2012 03:12 PM, Ben Bacarisse wrote:
    >>> It's good to see that the change is just due to a change in terminology.

    >>
    >> Could you explain the change in terminology? I thought about the
    >> possibility that something like that was involved, but I didn't notice
    >> anything relevant.

    >
    > I didn't notice it either, but as Tim Rentsch pointed out, the change
    > is in 6.2.5p1.


    Ah - that explains why I didn't know what Ben was talking about. I've
    got Tim killfiled - not because he's a spammer or any other kind of
    idiot, but just because I found that I frequently ended up wasting lots
    of time in long discussions with him disagreeing about excessively
    subtle issues without either of us ever managing to change the mind of
    the other. That would not have mattered if the discussions had been fun,
    or at least interesting, but I just found them frustrating - which could
    just as easily have been my fault as his.
    James Kuyper, Mar 27, 2012
    #10
  11. On 27.03.2012 16:32, Francois Grieu wrote:
    > I just tested my code for portability problems on systems
    > where int is 16-bit, and found one such system where […]


    Which system?

    Philipp
    Philipp Klaus Krause, Mar 28, 2012
    #11
  12. On 27/03/2012 19:32, Tim Rentsch wrote:
    > Francois Grieu <> writes:
    >
    >> I just tested my code for portability problems on systems
    >> where int is 16-bit, and found one such system where
    >>
    >> #include <stdio.h>
    >> int main(void)
    >> {
    >> printf("%d\n", 0<(unsigned short)0x8000 );
    >> return 0;
    >> }
    >>
    >> outputs 0. If I change 0< to 0u<, the output is 1.
    >>
    >> Is that a bug in this compiler, w.r.t. C99?

    >
    > Yes. There is a simple argument that it is:
    >
    > 1. The value 0x8000 is always representable as an unsigned short.
    >
    > 2. The rules for 'integer promotions' always preserve value.
    >
    > 3. The rules for 'usual arithmetic conversions' preserve value
    > for all operand values that are non-negative. (A negative
    > operand value might become non-negative due to UAC, but
    > all non-negative values don't change under UAC.)
    >
    > Since 0 < 0x8000 must hold, 0 < (unsigned short) 0x8000 must hold,
    > because ultimately the same mathematical comparison is done.


    Nice, reusable argument. Thanks.

    >
    >
    >> As an aside, 6.5.8p2 puzzles me; [snip]
    >>
    >> As an aside of the aside, I do not understand the third option in
    >> the constraint, and it has been removed in N1570.

    >
    > The removal is not a change but just indicative of a change in
    > nomenclature. In C99, there are incomplete types and object
    > types. In N1570, there are incomplete object types and complete
    > object types, which together make up object types. So in N1570,
    > both complete and incomplete types are included under the now
    > more general 'object type' term, and hence the third option in
    > C99 is folded into the second option in N1570.


    Again, thanks for the synthetic explanation.

    Francois Grieu
    Francois Grieu, Mar 28, 2012
    #12
  13. On 28/03/2012 09:07, Philipp Klaus Krause wrote:
    > On 27.03.2012 16:32, Francois Grieu wrote:
    >> I just tested my code for portability problems on systems
    >> where int is 16-bit, and found one such system where […]

    >
    > Which system?


    The CPU was a regular PC, running cmd.exe of Windows XP SP3; the
    failing compiler was Turbo C++ 1.01 (released 1991) in C mode.
    I am NOT using that compiler for anything but test.
    I there is a more modern 80x86 16-bit compiler (defined as
    UINT_MAX==0xFFFF, perhaps as an option), I want to know.

    My usual 16-bit targets include
    - ST7/ST19/ST21/ST23
    - PIC18
    - 8051 and derivatives
    and (at least with the compilers I now use for production)
    pass this test.

    My customer's platform include
    - 68K derivatives (including ColdFire)
    - ARM derivatives
    - MIPS derivatives
    - 80x86 derivatives
    For some of my customer's target systems, the option exist
    to compile with a 16-bit int (e.g. gcc -mshort ), and I have
    no idea of my customer's choice.

    Francois Grieu
    Francois Grieu, Mar 28, 2012
    #13
  14. Francois Grieu

    Kaz Kylheku Guest

    On 2012-03-28, Francois Grieu <> wrote:
    > On 28/03/2012 09:07, Philipp Klaus Krause wrote:
    >> On 27.03.2012 16:32, Francois Grieu wrote:
    >>> I just tested my code for portability problems on systems
    >>> where int is 16-bit, and found one such system where […]

    >>
    >> Which system?

    >
    > The CPU was a regular PC, running cmd.exe of Windows XP SP3; the
    > failing compiler was Turbo C++ 1.01 (released 1991) in C mode.


    Hahaha, you wasted everyone's time answering questions about
    irrelevant old cruft. Good job!
    Kaz Kylheku, Mar 28, 2012
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Timo Freiberger
    Replies:
    3
    Views:
    942
    Bob Hairgrove
    Oct 30, 2004
  2. Replies:
    4
    Views:
    818
    Kaz Kylheku
    Oct 17, 2006
  3. fancyerii
    Replies:
    21
    Views:
    1,484
    Roedy Green
    Nov 5, 2007
  4. Ioannis Vranos

    unsigned short, short literals

    Ioannis Vranos, Mar 4, 2008, in forum: C Programming
    Replies:
    5
    Views:
    674
    Eric Sosman
    Mar 5, 2008
  5. pozz
    Replies:
    12
    Views:
    738
    Tim Rentsch
    Mar 20, 2011
Loading...

Share This Page