• Representing signed values in corresponding unsigned types

    From NS@ns@2b.here to comp.lang.c on Sun Jun 28 10:11:30 2026
    From Newsgroup: comp.lang.c

    Can all the values of a signed int be represented in an unsigned int (or signed long in an unsigned long, etc.)? If INT_MIN is now 0 and all values are arithmetically increased by -INT_MIN, is it guaranteed that every signed
    value can be represented? In other words: is UINT_MAX >= INT_MAX - INT_MIN?

    It seems obvious, but the standard appears to imply that this is not guaranteed. It states:

    6.2.5.6

    For each of the signed integer types, there is a corresponding (but >different) unsigned integer type that uses the same amount of storage >(including sign information) and has the same alignment requirements.

    So, same amount of storage including sign bit implies UINT_MAX == INT_MAX*2

    But:

    6.2.6.2

    For unsigned integer types, the bits of the object representation shall be >divided into two groups: value bits and padding bits.
    [...]
    For signed integer types, the bits of an object representation shall be >divided into three groups: value bits, padding bits and the sign bit. [...] >(if there are M value bits in the signed type and N in the unsigned type, >then M <= N).

    But shouldn't it be M < N? Otherwise, if M == N, there are the same amount of value bits in both signed and unsigned (implying the unsigned has more
    padding bits), and UINT_MAX == INT_MAX. I doubt that this is ever done,
    but the standard seems to permit it.

    Although "The range of nonnegative values of a signed integer type is a subrange of its corresponding unsigned integer type" [6.2.5.9] the use of 'subrange' implies that M < N?


    So, is it actually portable to represent signed integers as unsigned?
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de (Stefan Ram) to comp.lang.c on Sun Jun 28 12:07:59 2026
    From Newsgroup: comp.lang.c

    NS <ns@2b.here> wrote or quoted:
    Although "The range of nonnegative values of a signed integer type is a >subrange of its corresponding unsigned integer type" [6.2.5.9] the use of >'subrange' implies that M < N?

    I take a "subrange" to be a range that is a subset,
    which would imply M <= N.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jun 28 06:52:23 2026
    From Newsgroup: comp.lang.c

    NS <ns@2b.here> writes:

    Can all the values of a signed int be represented in an unsigned
    int (or signed long in an unsigned long, etc.)?

    Not guaranteed.

    If INT_MIN is now 0 and
    all values are arithmetically increased by -INT_MIN, is it
    guaranteed that every signed value can be represented?

    No.

    In other words: is UINT_MAX >= INT_MAX - INT_MIN?

    Not guaranteed. The case of UINT_MAX == INT_MAX is allowed.

    It seems obvious, but the standard appears to imply that this is
    not guaranteed. It states:

    The standard allows implementations where UINT_MAX == INT_MAX.

    6.2.5.6

    For each of the signed integer types, there is a corresponding (but
    different) unsigned integer type that uses the same amount of storage
    (including sign information) and has the same alignment requirements.

    So, same amount of storage including sign bit implies
    UINT_MAX == INT_MAX*2

    It doesn't, because integer types (other than unsigned char) are
    allowed to have padding bits, and unsigned types may have more
    padding bits than signed types have.

    But:

    6.2.6.2

    For unsigned integer types, the bits of the object representation
    shall be divided into two groups: value bits and padding bits.
    [...]

    For signed integer types, the bits of an object representation
    shall be divided into three groups: value bits, padding bits and
    the sign bit. [...] (if there are M value bits in the signed type
    and N in the unsigned type, then M <= N).

    But shouldn't it be M < N?

    The standard means what it says. The case of M == N is allowed.

    Otherwise, if M == N, there are the same
    amount of value bits in both signed and unsigned (implying the
    unsigned has more padding bits), and UINT_MAX == INT_MAX.

    That's right.

    I doubt that this is ever done,

    It has been.

    but the standard seems to permit it.

    Yes, deliberately so.

    Although "The range of nonnegative values of a signed integer type
    is a subrange of its corresponding unsigned integer type" [6.2.5.9]
    the use of 'subrange' implies that M < N?

    Normal usage for "subrange" includes the case where the two ranges
    are the same.

    So, is it actually portable to represent signed integers as
    unsigned?

    Not fully portable, no. If it's important that this be true then
    somewhere there should be a test that INT_MAX < UINT_MAX, etc.

    (Note that as of C23 there are different rules, and indeed the newer
    standard requires, IIUC, that unsigned types have the same number of
    padding bits as their corresponding signed types, and consequently
    that INT_MAX < UINT_MAX, etc.)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sun Jun 28 18:27:20 2026
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    NS <ns@2b.here> writes:
    Can all the values of a signed int be represented in an unsigned
    int (or signed long in an unsigned long, etc.)?

    Obviously not. Negative values cannot be represented in unsigned types.
    But I don't think that's what NS meant.

    I believe it follows from the *current* rules (C23) that the number
    of representable values in signed int is exactly the same as the
    number of representable values in unsigned int. It also follows
    that each possible value of type signed int yields a unique value
    when converted to unsigned int. (The reverse conversion is still implementation-defined.)

    That wasn't the case in C17 and earlier. It was allowed, for
    example, for signed int and unsigned int to have the same size,
    with signed int having no padding bits and unsigned int having one
    (in place of the sign bit). Thus you could have INT_MAX==UINT_MAX.

    Such implementations were quite unusual, which presumably is why the
    committee was able to make them non-conforming in C23.

    [...]

    It doesn't, because integer types (other than unsigned char) are
    allowed to have padding bits, and unsigned types may have more
    padding bits than signed types have.

    Neither unsigned char nor signed char is allowed to have padding
    bits, going back at least to C99. (I believe C99 is when the concept
    of padding bits was introduced.) This implies that plain char also
    is not allowed to have padding bits)

    [...]

    So, is it actually portable to represent signed integers as
    unsigned?

    Not fully portable, no. If it's important that this be true then
    somewhere there should be a test that INT_MAX < UINT_MAX, etc.

    (Note that as of C23 there are different rules, and indeed the newer
    standard requires, IIUC, that unsigned types have the same number of
    padding bits as their corresponding signed types, and consequently
    that INT_MAX < UINT_MAX, etc.)

    Note that C23 is the current ISO C standard, and C23 with extensions
    is the default dialect for recent releases of gcc and clang.

    Of course earlier editions are still relevant.

    I believe you're correct about the change in requirements
    for C23. Most obviously, C23 requires 2's-complement for all
    signed types (prior editions also allowed sign-and-magnitude and ones'-complement).

    N3220 6.2.6.2p2 says:

    For signed integer types, the bits of the object representation
    shall be divided into three groups: value bits, padding bits,
    and the sign bit. If the corresponding unsigned type has width
    N, the signed type uses the same number of N bits, its *width*,
    as value bits and sign bit.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Sun Jun 28 22:23:00 2026
    From Newsgroup: comp.lang.c

    On 2026-06-28 06:11, NS wrote:
    Can all the values of a signed int be represented in an unsigned int (or signed long in an unsigned long, etc.)?

    Of course not. Signed int can represent negative values, unsigned int
    cannot.

    However, "The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the
    representation of the same value in each type is the same."(6.2.5p11)

    I see that you've quoted this same clause below, but cut off the
    citation just before it provided the fullest possible answer to your
    first question above.

    ...
    6.2.5.6

    For each of the signed integer types, there is a corresponding (but
    different) unsigned integer type that uses the same amount of storage
    (including sign information) and has the same alignment requirements.

    So, same amount of storage including sign bit implies UINT_MAX == INT_MAX*2

    No, first of all, the same amount of storage does not require that the
    number of bits be the same. secondly, if the number of bits were the
    same, then the relationship would be UINT_MAX == INT_MAX*2U+1U. The U's
    ensure that the calculations are carried out using unsigned int;
    otherwise the expression would produce unsigned overflow.

    ...
    6.2.6.2

    For unsigned integer types, the bits of the object representation shall be >> divided into two groups: value bits and padding bits.
    [...]
    For signed integer types, the bits of an object representation shall be
    divided into three groups: value bits, padding bits and the sign bit. [...] >> (if there are M value bits in the signed type and N in the unsigned type,
    then M <= N).

    But shouldn't it be M < N? Otherwise, if M == N, there are the same amount of value bits in both signed and unsigned (implying the unsigned has more padding bits), and UINT_MAX == INT_MAX. I doubt that this is ever done,
    but the standard seems to permit it.

    It does.
    I've heard of a system which did not have an arithmetic unit, but did
    have a floating point unit, which used the mantissa of floating point
    values to represent a large integer type. I think they would have had
    INT_MAX == UINT_MAX, but I can't vouch for that personally.

    Although "The range of nonnegative values of a signed integer type is a subrange of its corresponding unsigned integer type" [6.2.5.9] the use of 'subrange' implies that M < N?
    You're thinking of a "proper subrange", by analogy with the term "proper subset", but the standard does not require that the subrange be proper.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sun Jun 28 20:24:20 2026
    From Newsgroup: comp.lang.c

    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 2026-06-28 06:11, NS wrote:
    [...]
    But shouldn't it be M < N? Otherwise, if M == N, there are the same amount of
    value bits in both signed and unsigned (implying the unsigned has more
    padding bits), and UINT_MAX == INT_MAX. I doubt that this is ever done,
    but the standard seems to permit it.

    It does.
    I've heard of a system which did not have an arithmetic unit, but did
    have a floating point unit, which used the mantissa of floating point
    values to represent a large integer type. I think they would have had
    INT_MAX == UINT_MAX, but I can't vouch for that personally.

    That's allowed in C17 and earlier, but not (I think) in C23. Though
    it's possible that I'm misinterpreting the C23 wording (which I've
    quoted elsethread).

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Mon Jun 29 12:13:14 2026
    From Newsgroup: comp.lang.c

    In article <111shlo$3vq40$2@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    NS <ns@2b.here> writes:
    Can all the values of a signed int be represented in an unsigned
    int (or signed long in an unsigned long, etc.)?

    Obviously not. Negative values cannot be represented in unsigned types.
    But I don't think that's what NS meant.

    Agreed. I believe NS's question could be rephrased as, "does
    the standard guarantee that there is an injection from values of
    type signed int to values of unsigned int?"

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2