Signed int range confusion - c

This question might be very basic but i post here only after days of googling and for my proper basic understanding of signed integers in C.
Actually some say signed int has range
-32767 to 32767 and others say it has range
-32768 to 32767
Let us have int a=5 (signed / let us consider just 1 byte)
*the 1st representation of a=5 is represented as 00000101 as a positive number and a=-5 is represented as 10000101 (so range -32767 to 32767 justified)
(here the msb/sign bit is 1/0 the number will be positive/negative and rest(magnitude bits) are unchanged )
*the 2nd representation of a=5 is represented as 00000101 as a positive number and a=-5 is represented as 11111011
(the msb is considered as -128 and the rest of bits are manipulated to obtain -5) (so range -32768 to 32767 justified)
So I confuse between these two things. My doubt is what is the actual range of signed int in c ,1) or 2)

It depends on your environment and typically int can store -2147483648 to 2147483647 if it is 32-bit long and two's complement is used, but C specification says that int can store at least -32767 to 32767.
Quote from N1256 5.2.4.2.1 Sizes of integer types <limits.h>
Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.
— minimum value for an object of type int
INT_MIN -32767 // −(2 15 − 1)
— maximum value for an object of type int
INT_MAX +32767 // 2 15 − 1`

Today, signed ints are usually done in two's complement notation.
The highest bit is the "sign bit", it is set for all negative numbers.
This means you have seven bits to represent different values.
With the highest bit unset, you can (with 16 bits total) represent the values 0..32767.
With the highest bit set, and because you already have a representation for zero, you can represent the values -1..-32768.
This is, however, implementation-defined, other representations do exist as well. The actual range limits for signed integers on your platform / for your compiler are the ones found in your environment's <limits.h>. That is the only definite authority.
On today's desktop systems, an int is usually 32 or 64 bits wide, for a correspondingly much larger range than the 16-bit 32767 / 32768 you are talking of. So either those people are talking about really old platforms, really old knowledge, embedded systems, or the minimum guaranteed range -- the standard states that INT_MIN must be at least -32767, and INT_MAX be at least +32767, the lowest common denominator.

My doubt is what is the actual range of signed int in c ,1) [-32767 to 32767] or 2) [-32768 to 32767]?
The whole point of C and its advantage of high portability to old and new platforms is that code should not care.
C defines the range of int with 2 macros: INT_MIN and INT_MAX. The C spec specifies:
INT_MIN is -32,767 or less.
INT_MAX is +32,767 or more.
If code needs a 16-bit 2's complement type, use int16_t. If code needs a 32-bit or wider type, use long or int32least_t, etc. Do not code assuming int is something that it is not defined to be.

The value 32767 is the maximum positive value you can represent on a signed 16-bit integer. The C corresponding type is short.
The int type is represented on at least the same number of bytes as short and at most the same number of bytes as long. The size of int on 16-bit processors is 2 bytes (the same as short). On 32-bit and higher architecture, the size of int is 4 bytes (the same as long).
No matter the architecture, the minumum value of int is INT_MIN and the maximum value of int is INT_MAX.
Similar, there are constants to get the minimum and maximum values for short (SHRT_MIN and SHRT_MAX), long, char etc. You don't need to use hardcoded constants or to guess what is the minimum value for int on your system.
The representation #1 is named "sign and magnitude representation". It is a theoretical model that uses the most significant byte to store the sign and the rest of the bytes to store the absolute value of the number. It was used by some early computers, probably because it seemed a natural map of the numbers representation in mathematics. However, it is not natural for binary computers.
The representation #2 is named two's complement. The two's-complement system has the advantage that the fundamental arithmetic operations of addition, subtraction, and multiplication are identical to those for unsigned binary numbers (as long as the inputs are represented in the same number of bits and any overflow beyond those bits is discarded from the result). This is why it is the preferred encoding nowadays.

The C standard specifies the lowest limits for integer values. As it is written in the Standard (5.2.4.2.1 Sizes of integer types )
...Their implementation-defined values shall be equal or greater in
magnitude (absolute value) to those shown, with the same sign.
For objects of type int these lowest limits are
— minimum value for an object of type int
INT_MIN -32767 // −(215 − 1)
— maximum value for an object of type int
INT_MAX +32767 // 215 − 1
For the two's complement representation of integers the number of positive values is one less than the number of negative values. So if only tow bytes are used for representations of objects of type int then INT_MIN will be equal to -32768.
Take into account that 32768 in magnitude is greater than the value used in the Standard. So it satisfies the Standard requirement.
On the other habd for the representation "sign and magnitude" the limits (when 2 bytes are used) will be the same as shown in the Standard that is -32767:32767
So the actual limits used in the implementation depend on the width of integers and their representation.

Related

Representation of int in memory

On architectures where int is represented using multiple bytes in memory, what constraints does the C Standard impose regarding possible representations? Most current systems use either little-endian or big-endian representations, but it is possible to have a conforming system with a different representation? How different can it be?
what constraints does the C Standard impose regarding possible representations?
3 Encodings allowed: 2's complement, 1s' complement, sign-magnitude. Non-2's complement could have either a -0 or a trap representation.
int must be 16-bit or wider (a range of at least [-32767...32767]). Could be 36 or 64 for real historic examples.
but it is possible to have a conforming system with a different representation?
Sample: PDP-endian
0x01020304 stored as 2, 1, 4, 3. See also #chqrlie.
How different can it be?
int may have padding, char cannot. I do not know of any int with padding.
int could be 1 "byte" when a "byte" is more than 16 bits.
IIRC, some graphics processors used 64-bit "byte", char, int, long, long long.
I once did used a 64-bit long, unsigned long where the unsigned long had 1 padding bit such that ULONG_MAX == LONG_MAX. Compliant but unusual. In theory, UINT_MAX == INT_MAX is possible - never heard of such an implementation.
In 2020, I suspect the follow are universal.
Endian: either big or little.
2's complement. (Next C might require this.)
"byte size" of 8 (maybe 16, 32), int is 16 or 32 bit.
No padding.
From the following citations from the standard, we see:
int has at least 16 bits.
Any ordering of bytes is permissible.
Any ordering of bits is permissible (but must match unsigned int).
The value bits are binary.
Negative values use one of the three specified methods.
C 2018 6.2.6.1 says:
1 The representations of all types are unspecified except as stated in this subclause.
2 Except for bit-fields, objects are composed of contiguous sequences of one or more bytes, the number, order, and encoding of which are either explicitly specified or implementation-defined.
4 Values stored in non-bit-field objects of any other object type [other than unsigned bit-fields and unsigned char, addressed in paragraph 3] consist of n × CHAR_BIT bits, where n is the size of an object of that type, in bytes…
6.2.6.2 says:
1 For unsigned integer types other than unsigned char,… If there are N value bits, each bit shall represent a different power of 2 between 1 and 2N-1, so that objects of that type shall be capable of representing values from 0 to 2N − 1 using a pure binary representation;…
2 For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N ). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:
— the corresponding value with sign bit 0 is negated (sign and magnitude);
— the sign bit has the value −(2M ) (two’s complement);
— the sign bit has the value −(2M − 1) (ones’ complement).
Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value. In the case of sign and magnitude and ones’ complement, if this representation is a normal value it is called a negative zero.
5 The values of any padding bits are unspecified… For any integer type, the object representation where all the bits are zero shall be a representation of the value zero in that type.
And 5.2.4.2.1 tells us int must be able to represent at least −32767 to +32767, from which we deduce it has at least 15 value bits.

Signed integer and unsigned integer [duplicate]

This question already has answers here:
Why Do We have unsigned and signed int type in C?
(4 answers)
Closed 4 years ago.
I am studying c language and there are two different integer types, signed/unsigned.
Signed integers can present both positive and negative numbers. Why do we
need unsigned integers then?
One word answer is "range"!
When you declare a signed integer, it takes 4 bytes/ 32 bits memory (on a 32 bit system).
Out of those 32 bits, 1 bit is for sign and other 31 bits represent number. Means you can represent any number between -2,147,483,648 to 2,147,483,647 i.e. 2^31.
What if you want to use 2,147,483,648? Go for 8 bytes? But if you are not interested in negative numbers, then isn't it wastage of 4 bytes?
If you use unsigned int, all 32bits represent your number as you don't need to spare 1 bit for sign. Hence, with unsigned int, you can go from 0 to 4,294,967,295 i.e. 2^32
Same applies for other data types.
The reason is that Integer always has a fixed size. On most systems, an Integer is 32 bits large.
So no matter of having a signed or unsigned Integer, it takes always the same amout of memory. And that's where signed and unsigned differ: the range
Where an unsigned integer has a range of 0 to 4294967295 (2³²-1), the signed integer has a range of -2147483647 to 2147483648
unsigned types have an extra bit of storage, allowing them a maximum magnitude of 2CHAR_BIT * sizeof(type)-1 for positive values. This is why types like size_t, which are meant to store sizes of files, strings, arrays, etc. are unsigned.
With signed integers, one bit is reserved for the sign, so if an int is 32-bits long, you only get 31 bits to store the magnitude of the number. An unsigned int does not have this restriction; the MSB is used for magnitude as well, but it comes at the expense of no longer being able to be negative.
Signed integer overflow is undefined by the C standard, whereas unsigned integer overflow is guaranteed to wrap-around and reset to zero. For example, the following code invokes undefined behavior in C:
int a = INT_MAX;
a++;
Whereas this is guaranteed to wrap-around back to zero:
unsigned int a = UINT_MAX;
a++;
Unsigned types are generally better for performing bit operations on
There are two/three reasons, given that C must offer the greatest range of possibilities to the programmer.
The first is that an unsigned integer can hold a double (positive) value in respect to its signed counterpart. And we don't want to waste any single bit right?
The second is that a protocol, or some data structure a program must cope with, can use unsigned values, so it is handy to have that data type.
The third is that processors actually have unsigned types, so C language gives them available. May be that there are algorithms which relay on overflow, for example.
There can be still other motivations, probably I don't remember them all.
Personally, I make large use of unsigned integers in embedded applications. For example, using a single unsigned char as an index into a circular buffer of 256 elements, makes it simple and fast to increment the index without checking for overflow, because when the index overflows, it does exactly what I want it to do (reset to zero). Again, there are probably many other situations, I tell just the first that comes to my mind.
It's all about memory. They are used to represent a greater number without making use of a larger amount of memory.
Numbers are stored on the computer in binary form. Signed numeric values use a process called two's complement to transform positive numbers into negative ones where the first bit, the one that could represent the highest value is not taken into account for any calculation.
It means that the numeric signed type of your choice can only store a maximum value of N available bits minus 1 bit and the remaining bit will be used to determine the sign of the value, while an unsigned type of your choice can make use of all its available bits to store its value with the drawback of not being able to represent negative values.

Is it always possible to convert an `int` to a `float`

Is a conversion from an int to a float always possible in C without the float becoming one of the special values like +Inf or -Inf?
AFAIK there is is no upper limit on the range of int.
I think a 128 bit int would cause an issue for a platform with an IEEE754 float as that has an upper value of around the 127th power of 2.
Short answer to your question: no, it is not always possible.
But it is worthwhile to go a little bit more into details. The following paragraph shows what the standard says about integer to floating-point conversions (online C11 standard draft):
6.3.1.4 Real floating and integer
2) When a value of integer type is converted to a real floating type,
if the value being converted can be represented exactly in the new
type, it is unchanged. If the value being converted is in the range of
values that can be represented but cannot be represented exactly, the
result is either the nearest higher or nearest lower representable
value, chosen in an implementation-defined manner. If the value being
converted is outside the range of values that can be represented, the
behavior is undefined. ...
So many integer values may be converted exactly. Some integer values may lose precision, yet a conversion is at least possible. For some values, however, the behaviour might be undefined (if, for example, an integer value would not be able to be represented with the maximum exponent of the float value). But actually I cannot assume a case where this will happen.
Is it always possible to convert an int to a float?
Reasonably - yes. An int will always convert to a finite float. The conversion may lose some precision for great int values.
Yet for the pedantic, an odd compiler could have trouble.
C allows for excessively wide int, not just 16, 32 or 64 bit ones and float could have a limit range, as small as 1e37.
It is not the upper range of int or INT_MAX that should be of concern. It is the lower end. INT_MIN which often has +1 greater magnitude than INT_MAX.
A 124 bit int min value could be about -1.06e37, so that does exceed the minimal float range.
With the common binary32 float, an int would need to be more than 128 bits to cause a float infinity.
So what test is needed to detect this rare situation?
Form an exact power-of-2 limit and perform careful math to avoid overflow or imprecision.
#if -INT_MAX == INT_MIN
// rare non 2's complement machine
#define INT_MAX_P1_HALF (INT_MAX/2 + 1)
_Static_assert(FLT_MAX/2 >= INT_MAX_P1_HALF, "non-2's comp.`int` range exceeds `float`");
#else
_Static_assert(-FLT_MAX <= INT_MIN, "2's complement `int` range exceeds `float`");
#endif
The standard only requires floating point representations to include a finite number as large as 1037 (§5.2.4.2.2/12) and does not put any limit on the maximum size of an integer. So if your implementation has 128-bit integers (or even 124-bit integers), it is possible for an integer-to-float conversion to exceed the range of finite representable floating point numbers.
No, it not always possible to convert an int to a float, due to how floats work. 32 bit floats greater than 16777216 (or less than -16777216) need to be even, greater than 33554432 (or less than -33554432) need to be evenly divisibly by 4, greater than 67108864 (or less than -67108864) need to be evenly divisibly by 8, etc. The IEEE-754 float standard defines round to nearest even as the default mode, but other modes exist depending upon implementation.
Also, the largest 128 bit int = 2^128 - 1 is greater than the largest 32 bit float = 2^127 x 1.11111111111111111111111 = 2^127 x (2-2^-23) = 2^127 x (2^1-2^-23) = 2^(127+1) - 2^(127-23) = 2^(127+1)-2^(127-23) = 2^(128) - 2^(104)

Issue with absolute value of 64 bit integer

This C code tries to find the absolute value of a negative number but the output also is negative. Can anyone tell me how to overcome this?
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
int main() {
int64_t a = 0x8000000000000000;
a = llabs(a);
printf("%" PRId64 "\n", a);
return 0;
}
Output
-9223372036854775808
UPDATE:
Thanks for all your answers. I understand that this is a non-standard value and that is why I am unable to perform an absolute operation on it. However, I did encounter this in an actual codebase that is a Genetic Programming simulation. The "organisms" in this do not know about the C standard and insist on generating this value :) Can anyone tell me an efficient way of working around this? Thanks again.
If the result of llabs() cannot be represented in the type long long, then the behaviour is undefined. We can infer that this is what's happening here - the out-of-range value 0x8000000000000000 is being converted to the value -9223372036854775808 when converted to int64_t, and your long long value is 64 bits wide, so the value 9223372036854775808 is unrepresentable.
In order for your program to have defined behaviour, you must ensure that the value passed to llabs() is not less than -LLONG_MAX. How you do this is up to you - either modify the "organisms" so that they cannot generate this value (eg. filter out those that create the out-of-range value as immediately unfit) or clamp the value before you pass it to llabs().
Basically, you can't.
The range of representable values for int64_t is -263 to +263-1. (And the standard requires int64_t to have a pure 2's-complement representation; if that's not supported, an implementation just won't define int64_t.)
That extra negative value has no corresponding representable positive value.
So unless your system has an integer type bigger than 64 bits, you're just not going to be able to represent the absolute value of 0x8000000000000000 as an integer.
In fact, your program's behavior is undefined according to the ISO C standard. Quoting section 7.22.6.1 of the N1570 draft of the 2011 ISO C standard:
The abs, labs, and llabs functions compute the absolute
value of an integer j. If the result cannot be represented, the
behavior is undefined.
For that matter, the result of
int64_t a = 0x8000000000000000;
is implementation-defined. Assuming long long is 64 bits, that constant is of type unsigned long long. It's implicitly converted to int64_t. It's very likely, but not guaranteed, that the stored value will be -263, or -9223372036854775808. (It's even permitted for the conversion to raise an implementation-defined signal, but that's not likely.)
(It's also theoretically possible for your program's behavior to be merely implementation-defined rather than undefined. If long long is wider than 64 bits, then the evaluation of llabs(a) is not undefined, but the conversion of the result back to int64_t is implementation-defined. In practice, I've never seen a C compiler with long long wider than 64 bits.)
If you really need to represent integer values that large, you might consider a multi-precision arithmetic package such as GNU GMP.
0x8000000000000000 is the smallest number that can be represented by a signed 64-bit integer. Because of quirks in two's complement, this is the only 64-bit integer with an absolute value that cannot be represented as a 64-bit signed integer.
This is because 0x8000000000000000 = -2^63, while the maximum representable 64-bit integer is 0x7FFFFFFFFFFFFFFF = 2^63-1.
Because of this, taking the absolute value of this is undefined behaviour that will generally result in the same value.
A signed 64-bit integer ranges from −(2^63) to 2^63 − 1, The absolute value of 0x8000000000000000, or −(2^63), is 2^63, is bigger than the max 64-bit integer.
No signed integer with its highest bit set high and all other bits low is representable in the same type as absolute value of that integer.
Observe an 8-bit integer
int8_t x = 0x80; // binary 1000_0000, decimal -128
An 8-bit signed integer can hold values between -128 and +127 inclusive, so the value +128 is out of range.
For a 16-bit integer this hols as well
int16_t = 0x8000; // binary 1000_0000_0000_0000, decimal -32,768
A 16-bit integer can hold values between -32,768 and +32,767 inclusive.
This pattern holds for any size integer as long as it is represented in two's complement, as is the de-facto representation for integers in computers. Two's complement holds 0 as all bits low and -1 as all bits high.
So an N-bit signed integer can hold values between 2^(N-1) and 2^(N-1)-1 inclusive, an unsigned integer can hold values between 0 and 2^N-1 inclusive.
Interestingly:
int64_t value = std::numeric_limits<int64_t>::max();
std::out << abs(value) << std::endl;
yields a value of 1 on gcc-9.
Frustrating!

Confusion in 16-bit data-type range

In a 16 Bit C compiler we have 2 bytes to store an integer, and 1 byte for a character. For unsigned integers the range is 0 to 65535. For signed integers the range is -32768 to 32767. For unsigned character, 0 to 255. According to the integer type, shouldn't the signed character range be like -128 to 127. But why -127 to 127? What about the remaining one bit?
I think you're mixing two things:
What ranges the standard requires for signed char, int etc.
What ranges are implemented in most hardware these days.
These don't necessarily have to be the same as long as the range implemented is a superset of the range required by the standard.
According to the C standard, the implementation-defined values of SCHAR_MIN and SCHAR_MAX shall be equal or greater in magnitude (absolute value) to, and of the same sign as:
SCHAR_MIN -127
SCHAR_MAX +127
i.e. only 255 values, not 256.
However, the limits defined by a compliant implementation can be 'greater' in magnitude than these. i.e. [-128,+127] is allowed by the standard too. And since most machines represent numbers in the 2's complement form, [-128,+127] is the range you will get to see most often.
Actually, even the minimum range of int defined by the C standard is symmetric about zero. It is:
INT_MIN -32767
INT_MAX +32767
i.e. only 65535 values, not 65536.
But again, most machines use 2's complement representation, and this means that they offer the range [-32768,+32767].
While in 2's complement form it is possible to represent 256 signed values in 8 bits (i.e. [-128,+127]), there are other signed number representations where this is not possible.
In the sign-magnitude representation, one bit is reserved for the sign, so:
00000000
10000000
both mean the same thing, i.e. 0 (or rather, +0 and -0).
This means, one value is wasted. And thus sign-magnitude representation can only hold values from -127 (11111111) to +127 (01111111) in 8 bits.
In the one's complement representation (negate by doing bitwise NOT):
00000000
11111111
both mean the same thing, i.e. 0.
Again, only values from -127 (10000000) to +127 (01111111) can be represented in 8 bits.
If the C standard required the range to be [-128,+127], then this would essentially exclude machines using such representations from being able to efficiently run C programs. They would require an additional bit to represent this range, thus needing 9 bits to store signed characters instead of 8. The logical conclusion based on the above is: This is why the C standard requires [-127,+127] for signed characters. i.e. to allow implementations the freedom to choose a form of integer representation that suits their needs and at the same time be able to adhere to the standard in an efficient way. The same logic applies to int as well.

Resources