How can I confirm the range of unsigned long integer in C? - c

unsigned long has 8 bytes on my Linux gcc.
unsigned long long has 8 bytes on my Linux gcc, too.
So I think the range of integers they can show is from 0 min to (2^64 - 1)max.
Now I want to confirm if I'm correct.
Here is my code:
#include <stdio.h>
int main(void)
{
printf("long takes up %d bytes:\n", sizeof(long));
printf("long long takes up %d bytes:\n", sizeof(long long));
unsigned long a = 18446744073709551615;
a++;
printf("a + 1 = %lu\n", a);
unsigned long long b = 18446744073709551615;
b++;
printf("b + 1 = %llu\n", b);
return 0;
}
However, the code cannot be compiled and I get the following warning:
warning: integer constant is so large that it is unsigned
Where did I do wrong? How can I modify the code ?

When you initialize num, you can append the "UL" for unsigned long and ULL for unsigned long long.
For example:
unsigned long a = 18446744073709551615UL;
unsigned long long b = 18446744073709551615ULL;
Also, use %zu instead of %d because sizeof return size_t.
According to cppreference:
integer-suffix, if provided, may contain one or both of the following (if both are provided, they may appear in any order:
unsigned-suffix (the character u or the character U)
long-suffix (the
character l or the character L) or the long-long-suffix (the character
sequence ll or the character sequence LL) (since C99)
C standard 5.2.4.2.1 Sizes of integer types <limits.h> :
1 The values given below shall be replaced by constant expressions suitable for use in #if preprocessing directives. Moreover, except for
CHAR_BIT and MB_LEN_MAX, the following shall be replaced by
expressions that have the same type as would an expression that is an
object of the corresponding type converted according to the integer
promotions. Their implementation-defined values shall be equal or
greater in magnitude (absolute value) to those shown, with the same
sign.

You find some useful definitions in <limits.h>.

Initialize unsigned numbers with -1. This will automatically be MAX value in C.
#include <stdio.h>
int main(void)
{
printf("long takes up %d bytes:\n", sizeof(long));
printf("long long takes up %d bytes:\n", sizeof(long long));
unsigned long a = -1;
printf("a = %lu\n", a);
unsigned long long b = -1;
printf("b = %llu\n", b);
return 0;
}
Update: Changed the code based on comments :)

How can I confirm the range of unsigned long integer in C?
Best, just use the macros from <limits.h>. It better self documents code's intent.
unsigned long long b_max = ULLONG_MAX;
Alternatively, assign -1 to the unsigned type. As -1 is not in the range of an unsigned type, it will get converted to the target type by adding the MAX value of that type plus 1. The works even on rare machines that have padding.
... if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. C11dr §6.3.1.3 2
The min values is of course 0 for an unsigned type.
unsigned long long b_min = 0;
unsigned long long b_max = -1;
printf("unsigned long long range [%llu %llu]\n", b_min, b_max);
Note that picky compilers will complain about assigning an out-of-range value with b_max = -1;. Use ULLONG_MAX.
Where did I do wrong?
The warning "warning: integer constant is so large that it is unsigned" is due to 18446744073709551615 is a integer decimal constant outside the long long range on your platform. Unadorned decimal constants are limited to that. Append a U or u. Then the compiler will consider unsigned long long.
unsigned long long b = 18446744073709551615u;
Further, there is no C spec that says 18446744073709551615 is the max value of unsigned long long. It must be at least that. It could be larger. So assigning b = 18446744073709551615u may not assign the max value.
How can I modify the code ?
Shown above

As rsp stated you can specify the type of the literal with UL and ULL.
But this won't lead to a conclusive result in your code for the arithmetics.
The value your print will always be 0 because
2^64 % 64 = 0 // 64 = 8 byte
2^64 % 32 = 0 // 32 = 4 byte
2^64 % 16 = 0 // 16 = 2 byte
as you can see the variable size always doubles so if you us a wrapping number for 8 bytes it just wraps multiple types on the smaller sizes and yields the same result.
The sizeof will show you the right values.
But generally you want to check for these things in code and not on output so you could use limits.h as suggested by Arndt Jonasson.
or you can use static_assert to check at compile time.

Related

Why does unsigned short (0xffff) print 65,535 and unsigned int (0xffffffff) print -1 in C?

I think the title explains pretty well what I'm asking so here is my code.
#include <stdio.h>
unsigned short u_short = 0xffff;
unsigned int u_int = 0xffffffff;
int main(){
printf("unsigned short = %d\n", u_short);
printf("unsigned int = %d\n", u_int);
return 0;
}
Here is my printout.
printout picture
printf("unsigned int = %d\n", u_int); is undefined behavior (UB) when u_int is out of the positive int range. Do not used "%d" to print unsigned.
Use printf("unsigned int = %u\n", u_int);
This is likely what happened in your C implementation:
In printf("unsigned short = %d\n", u_short);, the unsigned short value 65,535 is automatically converted to an int with the same value.1,2
The int value 65,535 is passed to printf, which formats it as “65535” due to the %d conversion specification.
In printf("unsigned int = %d\n", u_int);, the unsigned int value 4,294,967,295 is passed to printf; it is not converted to an int. As an unsigned int, 4,294,967,295 is represented with 32 one bits.
Because of the %d conversion specification, printf seeks an int value that was passed as an argument. For this, it finds the bits passed for your unsigned int, because an unsigned int and an int are passed in the same place in your C implementation, so the printf looking for an int finds the bits in the same place the calling routine put the unsigned int bits.3
When interpreted as an int type, these bits, 32 ones, represent the value −1.3 Given the −1 value, printf formats it as “-1” due to the %d conversion specification.
Footnotes
1 In many places in expressions, including arguments corresponding to ... of a function declaration, values of types narrower than int are automatically promoted to int, as part of the integer promotions.
2 A C implementation could have an unsigned short as wide as an int, in which case this conversion would not occur. That is rare these days.
3 This is a description of what likely happened in your C implementation. The behavior is not defined by the C standard and may vary in other C implementations or even in different programs in your C implementation.
printf has some anomalies due to the usual argument promotions. In particular, arguments of type char and short are promoted to int when passing them to printf. Usually this is fine, but sometimes it results in surprises like these. What you get when you promote an unsigned 16-bit 0xffff to 32 bits is not 0xffffffff.
printf has some relatively little-known and relatively rarely-used modifiers to, in effect, undo those promotions and print char and short arguments as what they "really were". So you'll see more-consistent results if you tell printf that you were actually passing a short, like this:
printf("unsigned short = %hd\n", u_short);
printf("unsigned int = %d\n", u_int);
Now printf knows that the argument in the first call was really a short, so it treats it as such. On my machine, this now prints
unsigned short = -1
unsigned int = -1
(Now, with that said, it's arguably a bad idea to print unsigned integers with %d, as the other answers and comments have explained.)

Clarifications about unsigned type in C

Hi I'm currently learning C and there's something that I quite don't understand.
First of all I was told that if I did this:
unsigned int c2 = -1;
printf("c2 = %u\n", c2);
It would output 255, according to this table:
But I get a weird result: c2 = 4294967295
Now what's weirder is that this works:
unsigned char c2 = -1;
printf("c2 = %d\n", c2);
But I don't understand because since a char is, well, a char why does it even print anything? Since the specifier here is %d and not %u as it should be for unsigned types.
The following code:
unsigned int c2 = -1;
printf("c2 = %u\n", c2);
Will never print 255. The table you are looking at is referring to an unsigned integer of 8 bits. An int in C needs to be at least 16 bits in order to comply with the C standard (UINT_MAX defined as 2^16-1 in paragraph §5.2.4.2.1, page 22 here). Therefore the value you will see is going to be a much larger number than 255. The most common implementations use 32 bits for an int, and in that case you'll see 4294967295 (2^32 - 1).
You can check how many bits are used for any kind of variable on your system by doing sizeof(type_or_variable) * CHAR_BIT (CHAR_BIT is defined in limits.h and represents the number of bits per byte, which is again most of the times 8).
The correct code to obtain 255 as output is:
unsigned char c = -1;
printf("c = %hhu\n", c);
Where the hh prefix specifier means (from man 3 printf):
hh: A following integer conversion corresponds to a signed char or unsigned char argument, or a following n conversion corresponds to a pointer to a signed char argument.
Anything else is just implementation defined or even worse undefined behavior.
In this declaration
unsigned char c2 = -1;
the internal representation of -1 is truncated to one byte and interpreted as unsigned char. That is all bits of the object c2 are set.
In this call
printf("c2 = %d\n", c2);
the argument that has the type unsigned char is promoted to the type int preserving its value that is 255. This value is outputted as an integer.
Is this declaration
unsigned int c2 = -1;
there is no truncation. The integer value -1 that usually occupies 4 bytes (according to the size of the type int) is interpreted as an unsigned value with all bits set.
So in this call
printf("c2 = %u\n", c2);
there is outputted the maximum value of the type unsigned int. It is the maximum value because all bits in the internal representation are set. The conversion from signed integer type to a larger unsigned integer type preserve the sign propagating it to the width of the unsigned integer object.
In C integer can have multiple representations, so multiple storage sizes and value ranges
refer to the table below for more details.

how can something be bigger than (unsigned long long) > LONG_MAX?

I found this code in an algorithm I need to update:
if (value > (unsigned long long) LONG_MAX)
EDIT: value is the result of a division of two uint64_t numbers.
I understand that (unsigned long long) LONG_MAX is a VERY big number:
#include "stdio.h"
#include "limits.h"
int main() {
unsigned long long ull = (unsigned long long) LONG_MAX;
printf("%lu",ull);
return 0;
}
prints 9223372036854775807
So what I am comparing here? In what case this if statement will evaluate to true?
A float or double can be larger than that. Appendix Ep5 of the C standard states that either type must be able to hold a value at least as large as 1E37 which is a larger value than LONG_MAX which must be at least 2147483647:
The values given in the following list shall be replaced by
implementation-defined constant expressions with values that are
greater than or equal to those shown:
#define DBL_MAX 1E+37
#define FLT_MAX 1E+37
#define LDBL_MAX 1E+37
So if value is either of those types, it could evaluate to true.
EDIT:
Since value is a uint64_t, whose max value is 18446744073709551615, this can also be larger than LONG_MAX.
how can something be bigger than (unsigned long long) > LONG_MAX?
Easily. LONG MAX is the maximum value that can be represented as a long int. Converting that to unsigned long long does not change its value, only its data type. The maximum value that can be represented as an unsigned long int is larger on every C implementation you're likely to meet. The maximum value of long long int is larger on some, and the maximum value of unsigned long long int is, again, larger on every C implementation you're likely to meet (much larger on some).
However, this ...
unsigned long long ull = (unsigned long long) LONG_MAX;
printf("%lu",ull);
... is not a conforming way to investigate the value in question because %lu is a formatting directive for type unsigned long, not unsigned long long. The printf call presented therefore has undefined behavior.
I found this code in an algorithm I need to update:
if (value > (unsigned long long) LONG_MAX)
EDIT: value is the result of a division of two uint64_t numbers.
[...]
In what case this if statement will
evaluate to true?
Supposing that value has type uint64_t, which is probably the same as unsigned long long in your implementation, the condition in that if statement will evaluate to true at least when the most-significant bit of value is set. If your long int is only 32 bits wide, however, then the condition will evaluate to true much more widely than that, because there are many 64-bit integers that are larger than the largest value representable as a signed 32-bit integer.
I would be inclined to guess that the code was indeed written under the assumption that long int is 32 bits wide, so that the if statement asks a very natural question: "can the result of the previous uint64_t division be represented as a long?" In fact, that's what the if statement is evaluating in any case, but it makes more sense if long is only 32 bits wide, which is typical of 32-bit computers and standard on both 32- and 64-bit Windows.
LONG_MAX is maximum value for a signed long. By standard it should be >= +2147483647 (fits in 32 bit signed)
There's also ULLONG_MAX for unsigned long long, which is currently most often 18446744073709551615. The standard mandates that to be >= than 9223372036854775807

I cant understand why is 9 power 13 does not give a correct answer

#include <stdio.h>
#include <limits.h>
int main() {
printf("The minimum value of LONG = %ld\n", LONG_MIN);
printf("The maximum value of LONG = %ld\n", LONG_MAX);
printf("%ld",9*9*9*9*9*9*9*9*9*9*9*9*9);
return(0);
}
Sorry for the basic question but I am currently struggling to assign a large number to a variable even though it is a long int. The result here of 9 power 13 is false. Normally it would be 2541865828329.
Output :
minimum value of LONG = -9223372036854775808
The maximum value of LONG = 9223372036854775807
3540156393
The product is actually of int type. You should explicitly convert it to long type by suffixing all the multipliers by LL.
9 * 9 .. => is of type int
9LL * 9LL .. => is of type long long
The program actually has undefined behavior:
The result is computed as an int because all operands in the expression are of type int. If type int is 32-bit wide, the computation causes arithmetic overflow, which has undefined behavior.
passing an int for printf format %d has undefined behavior too, especially if type long has a different size than type int.
Note that 913 is larger than 231-1 but smaller than 263-1, so type long, which is specified as having at least 31 value bits might not be large enough for this value, causing arithmetic overflow, which has undefined behavior.
Type long long is specified as having at least 63 value bits, so it is safe to perform the computation with this type.
The rules for type integer promotion and type conversion are somewhat tricky. You can read section 6.3 of the C Standard for details, just remember that for types int, long and long long, the computation is performed with the type of the larger operand and the result has this type.
All binary arithmetic operators have left to right associativity, which means a + b + c is evaluated as (a + b) + c. This rule applies to operators with the same precedence level, so a * b / c is evaluated as (a * b) / c, which does not apply here but is useful to know.
Form the above, we can derive that specifying the first operand of the expression as having type long long suffices for the expression to evaluate using long long arithmetic and produce a value of type long long. The value 9 can be explicitly converted to long long with a cast (long long)9 or with a suffix 9LL. Note that 9LL is less confusing than 9ll (nine ell ell in small case) that can be mis read as 911.
Here is a corrected program:
#include <stdio.h>
#include <limits.h>
int main() {
printf("The minimum value of LONG = %ld\n", LONG_MIN);
printf("The maximum value of LONG = %ld\n", LONG_MAX);
printf("The minimum value of LONG LONG = %lld\n", LLONG_MIN);
printf("The maximum value of LONG LONG = %lld\n", LLONG_MAX);
printf("9**13 = %lld\n", 9LL * 9 * 9 * 9 * 9 * 9 * 9 * 9 * 9 * 9 * 9 * 9 * 9);
return 0;
}
Output on 64-bit OS/X:
The minimum value of LONG = -9223372036854775808
The maximum value of LONG = 9223372036854775807
The minimum value of LONG LONG = -9223372036854775808
The maximum value of LONG LONG = 9223372036854775807
9**13 = 2541865828329
Output on a 32-bit system or a recent 64-bit Windows OS, with a conformant C library:
The minimum value of LONG = -2147483648
The maximum value of LONG = 2147483647
The minimum value of LONG LONG = -9223372036854775808
The maximum value of LONG LONG = 9223372036854775807
9**13 = 2541865828329
The issue is that 9 is not a long int. You need first to convert all your 9s to long, so instead use:
printf("%ld",9L*9L*9L*9L*9L*9L*9L*9L*9L*9L*9L*9L*9L);

C - int and long have the same size and max value

I want to check the size, max value, and min value of data type int, long and their unsigned form. The output of my program shows that both int and long have the same size, max, and min value, same goes to their unsigned form. Here is the output of my program:
Size of int : 4 byte, Max value : 2147483647, Min value : -2147483648
Size of long : 4 byte, Max value : 2147483647, Min value : -2147483648
Size of unsigned int : 4 byte, Max value : 4294967295, Min value : 0
Size of unsigned long : 4 byte, Max value : 4294967295, Min value : 0
And here is my code:
#include <stdio.h>
#include <limits.h>
int main(int argc, char *argv[])
{
printf("Size of int : %ld byte, Max value : %d, Min value : %d\n", sizeof(int), INT_MAX, INT_MIN);
printf("Size of long : %ld byte, Max value : %d, Min value : %d\n", sizeof(long), LONG_MAX, LONG_MIN);
printf("\n");
printf("Size of unsigned int : %ld byte, Max value : %u, Min value : %d\n", sizeof(unsigned int), UINT_MAX, 0);
printf("Size of unsigned long : %ld byte, Max value : %lu, Min value : %d\n", sizeof(unsigned long), ULONG_MAX, 0);
return 0;
}
My question is, is this normal that int and long have the same size, max value, and min value? I am running the program using gcc version 5.1.0 (tdm64-1) compiler on Windows 10 64-bit machine.
This relationship always hold:
short int <= int <= long int.
So in some cases int may have the same size as long, while in some other cases int may have the size as short. But int will never exceed long and will never fall below short. This is what the above statement(inequality) says.
In your case, int is equal to long.
The C standard does permit int and long to have the same size and range. It's easiest to explain the rules for unsigned types, which have minimum maximums: each unsigned integer type must be able to represent 0 through at least some number. This is the table:
type minimum maximum
unsigned char 255 (2**8 - 1)
unsigned short 65,535 (2**16 - 1)
unsigned int 65,535 (2**16 - 1)
unsigned long 4,294,967,295 (2**32 - 1)
unsigned long long 18,446,744,073,709,551,615 (2**64 - 1)
So you can see that a configuration in which unsigned int and unsigned long have the same range is perfectly allowed, as long as that range is at least as big as the minimum range for unsigned long. The signed types are required to have the same overall value range as their unsigned counterparts, but shifted so that almost exactly half of the values are negative — unfortunately it's not as simple as "−2n−1 … 2n−1 − 1", because the standard continues to permit non-twos-complement implementations even though nobody has manufactured a CPU that does that in many years.
It's possible that you thought long would be able to represent up to 263 − 1 because that's true on most "64-bit" operating systems. But "64-bit" Windows is an exception. Almost all operating systems that call themselves "64-bit" use what is known as an "LP64 ABI", in which the integer types and void * have these sizes:
sizeof(short) == 2
sizeof(int) == 4
sizeof(long) == 8
sizeof(long long) == 8
sizeof(void *) == 8
Windows instead uses an "LLP64" ABI, with
sizeof(short) == 2
sizeof(int) == 4
sizeof(long) == 4
sizeof(long long) == 8
sizeof(void *) == 8
This is for backward compatibility with 32-bit Windows, in which long and int are also the same size; Microsoft thought too much existing code would break if they changed the size of long.
(Note that having sizeof(void*) > sizeof(long) is, for reasons too complicated to get into here, forbidden by the original 1989 C standard. Because they were determined to use LLP64 for 64-bit Windows, Microsoft rammed a change into C99 to permit it, over literally everyone else's explicit objections. And then, for over a decade after C99 came out, they didn't bother implementing the C99 features (e.g. uintptr_t and %zu) that were supposed to substitute for the relaxed requirement, leading to cumulative man-years of extra work for people trying to write programs that work on both Windows and not-Windows. Not That I'm Bitter™.)
Yes this is perfectly normal. An architecture with a 32 bit int, and 32 bit long, an a 64 bit pointer is called an LLP64 data model and is favoured by Windows (despite an Intel CPU itself using a 48 bit pointer internally).
(A 64 bit Linux architecture uses a LP64 data model which has a 32 bit int, a 64 bit long, and a 64 bit pointer.)
The C standard states that the minimum range for an int is -32767 to +32767, and a long -2147483647 to +2147483647. So both schemes are compliant with this.
From the C Standard (6.2.5 Types)
8 For any two integer types with the same signedness and different
integer conversion rank (see 6.3.1.1), the range of values of the type
with smaller integer conversion rank is a subrange of the values of
the other type.
So all that is required is that the range of values of the type int would be no greater than the range of values of the type long.
And (6.3.1.1 Boolean, characters, and integers)
1 Every integer type has an integer conversion rank defined as
follows:
— The rank of long long int shall be greater than the rank of long
int, which shall be greater than the rank of int, which shall be
greater than the rank of short int, which shall be greater than the
rank of signed char.
So though objects of the type int and long can have the same representation and correspondingly the same range of values nevertheless the rank of the type long is higher than the rank of the type int.
For example if the type int and long have the same representation the type of the expression x + y in the code snippet below will be long.
int x = 0;
long y = 0;
printf( "%ld\n", x + Y );
Not only the int and long types can have the same representation. On 64-bit systems for example the type long int and long long int also can coincide.
For example the output of this program running at www.ideone.com
#include <stdio.h>
int main(void)
{
printf( "sizeof( long int ) = %zu\n", sizeof( long int ) );
printf( "sizeof( long long int ) = %zu\n", sizeof( long long int ) );
return 0;
}
is
sizeof( long int ) = 8
sizeof( long long int ) = 8

Resources