Writing the smallest (signed) 32-bit number as a C++ literal

191 views Asked by At

If I wished to use the <limits> header, it would be easy to write the smallest integer:

std::int32_t small = std::numeric_limits<std::int32_t>::min();

This makes small equal to -2147483648.

I'm working in a library where such numbers are spelled out. Thus it would be preferred to write

std::int32_t small = -2147483648;

I'm having trouble with this on Visual Studio, but when I read the C++ spec, I find it suggests that I can't do this for any compiler. The reason I think I can't do this is that the spec indicates that a integer-literal does not include the negative sign. The negative sign actually becomes a unary-minus later. So C++ processes the integer literal 2147483648. This number is too large to fit in an int (assuming int is 32-bits, which it is on the platform I'm considering), so it must be a long int (64-bits).

(Incidentally, this is where I run awry on visual studio. It appears to treat 2147483648 as an unsigned long, rather than a signed long, but this question is about the spec behavior)

From what I can see, there's no way to write the 32 bit number -2147483648 as a 32-bit literal. You have to make it a 64-bit literal first, 2147483648UL, then negate it. This seems like an oversight, but it also seems to compile everywhere (except Visual Studio), so what did I miss?

3

There are 3 answers

0
ShadowRanger On

Trivial solution is to bitwise invert the maximum value of the proper type. Given all the fixed width types are guaranteed to use two's complement representation, bitwise invert of the largest maximum value will always produce the most negative value (negation is the same as flipping all the bits and adding one; invert omits the adding one step, so you end up with a negative number one further from zero), so you can reliably do:

std::int32_t small = ~INT32_C(2147483647);

The INT32_C is function macro to make an integer constant of the correct size (it'll usually expand to nothing, making it ~2147483647, unless int on your system is not the same size as int32_t).

0
user2357112 On

An integer literal too big for an int gets interpreted as a larger type, even if you don't put an L on the literal. On platforms with 64-bit long, std::int32_t small = -2147483648; is treated as negating a long, then converting it to std::int32_t for the assignment. Neither an explicit L nor an explicit cast to std::int32_t is required.

On platforms with 32-bit long, 2147483648 doesn't fit in a long. As of C++11, it would be interpreted as a long long, but older standards leave the behavior undefined. Interpreting too-big literals as unsigned long was common before C++11, especially since older C standards mandated unsigned long. Your Visual Studio settings seem to be set to an older standard.

0
Pete Becker On

-2147483648 has two parts: the integer literal 2147483648 and the - sign which negates it. 2147483648 is too large to fit in a 32-bit int type, so the compiler treats it as a larger type. Negating it doesn't change the type, and the result of the negation still won't fit in an int.

The most straightforward approach is to ensure that you're dealing with an int:

int min_val = -2147483647 - 1;

This approach has the advantage of working for any twos-complement type. -MAX_VAL has the same type as MAX_VAL, and subtracting 1 gives you the minimum value.