1

Sometimes it does matter if the compiler target for a C compiler has two's complement representation of integers or not, and having the preprocessor making the detection can be useful.

Since the standard requires the MAX/MIN macros from limits.h and stdint.h to be expressions that can be used in preprocessor conditionals, I think that

#include <limits.h>
#if INT_MIN + INT_MAX == -1
# define HAVE_TWOS_COMPLEMENT 1
#endif

does the trick, since one's complement and sign/magnitude architectures have symmetrical value ranges for signed integers. The question is, am I missing something here or is there a better way to make such a test in a compiler-agnostic way?

Pearly
  • 113
  • 1
  • 5
  • 3
    Just by curiosity, where have you found a machine with one's complement? they are harder to find than a unicorn. Can you try `#if (+0 != -0)`? In theory this should return true on one's complement. – David Ranieri Nov 15 '20 at 09:12
  • The existence of stdint types like `int32_t` mean it has 2's complement integers. – Shawn Nov 15 '20 at 09:21
  • 1
    Also, the next version of C will require 2's complement,. – Shawn Nov 15 '20 at 09:24
  • @David: Well, the standard permits it. So portable code should either deal with it, and non-portable code should test for it and complain instead of doing weird stuff... – Pearly Nov 15 '20 at 10:02
  • @Shawn: Where did you deduce that int32_t means 2's complement? I have only the C201x working draft, but I didn't find that requirement. int32_t means we have a CHAR_BIT==8 machine, more or less, but where does it fix the integer representation? – Pearly Nov 15 '20 at 10:06
  • 1
    @Alex: partially... e.g. I'm not really convinced that e.g. testing `-0 != 0` on one's complement or sign/mag machine works as expected as number compares go by _value_ not bit pattern, AFAIK. Plus, some of the tests there might invoke UB with regard to integer overflow. But yes, the question is essentially a superset of mine. thnx. – Pearly Nov 15 '20 at 10:11
  • @Pearly It's part of the definition of the (optional) fixed-width types. They have to use 2's complement. 7.20.1.1 of the C11 and C17 drafts. – Shawn Nov 15 '20 at 10:35
  • @Shawn: Do you see anything in the standard that says if a C implementation has the `int32_t` two’s complement type, that the `int` type must also be two’s complement? – Eric Postpischil Nov 15 '20 at 11:36
  • @EricPostpischil No. It's C2X that requires 2's complement for all integers, not just the optional ones from stdint. But with current versions of C, if an implementation provides a intN_t type, it has to be 2's complement. – Shawn Nov 15 '20 at 11:48

1 Answers1

7

In two’s complement, −1 is encoded as 111...111.

In one’s complement, −1 is encoded as 111...110.

In sign-and-magnitude, −1 is encoded as 100..001.

Therefore, the following detects the encoding of the int type:

#if   (-1 & 3) == 1
    //  The encoding is sign-and-magnitude.
#elif (-1 & 3) == 2
    //  The encoding is one’s complement.
#elif (-1 & 3) == 3
    //  The encoding is two’s complement.
#else
    //  Not possible in the C standard.
#endif

The test offered in the question, INT_MIN + INT_MAX == -1, is not reliable because C 2018 6.2.6.2 2 permits “the value with sign bit 1 and all value bits zero” to be a trap representation, in which case INT_MIN is −(2M−1), where M is the number of value bits, and INT_MAX is 2M−1, so INT_MIN + INT_MAX is zero.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • @Eric: thnx for reminding me of `0x80...0` being a possible trap representation. Considering all the issues, bit masking on `-1` seems the best way to handle the issue. – Pearly Nov 15 '20 at 16:36