Why does gcc ignore the "unsigned" data type statements?

109 Views Asked by At

One of the numbers for testing my function needs 64 bits to be stored and since I'm coding in C I need an unsigned long long to store such a number. When I try to define a variabile with that data type and assign the number to it gcc will tell me "integer constant is so large that it is unsigned" even though I already specified it was.

Edit: the warning was removed by adding ULL at the end of the integer.

I tried debugging and everything in my function works except for those cases where the number needs 64 bits. In my function at some point I need to reverse the number in question. For that I made a new variabile called reversed and used the data type unsigned long long.

unsigned long long num = 18446744073709551615ULL;

...

unsigned long long reversed = 0, i;
for(i = num; i > 0; i /= 10) {
    reversed *= 10;
    reversed += i % 10;
}

Through the debugger I watched the process and the variabile reversed gets an overflow on the last cycle of the for, when it gets multiplied by 10. I tried changing "unsigned long long" to "unsigned long long int" but nothing changed. I also tried casting every single other number used to make additions and multiplications as unsigned long long but that didn't work.

Sorry for any lack of information

3

There are 3 best solutions below

0
Eric Postpischil On BEST ANSWER

When I try to define a variabile with that data type and assign the number to it gcc will tell me "integer constant is so large that it is unsigned" even though I already specified it was.

You did not specify the integer constant was unsigned. unsigned long long num = 18446744073709551615; specifies that num is unsigned. It says nothing about 18446744073709551615.

18446744073709551615 is an integer constant. The fact it is going to be assigned to num does not make it take on the properties of num. It is merely an integer constant with no indication of whether it should be unsigned or not.

C automatically assigns types to integer constants. For decimal constants with no suffix, it normally uses the first of int, long int, and long long int in which the value fits. Your value, 18,446,744,073,709,551,615 does not fit into any of those types. Technically, then, it falls into a strange category in the C standard: “If an integer constant cannot be represented by any type in its list and has no extended integer type, then the integer constant has no type.” (C 2018 6.4.4.1 6.) The compiler chose to give it the type unsigned long long int and is warning you about this problem.

Adding the ull suffix fixes this, as it denotes the type to be unsigned long long int, into which the value fits. Simply adding u would also work, as then the types that are automatically considered include unsigned long long int.

In my function at some point I need to reverse the number in question.

That will not work because the decimal numeral formed by reversing the decimal digits representing your number will not fit into the unsigned long long int that your C implementation uses. No arithmetic can compute the correct result because the correct result cannot be represented in the unsigned long long int type.

You could reverse the digits using a string of characters, or you could output the reversed digits by printing them one at a time (or a few at a time) instead of having the entire number all at once.

0
giulio On

The warning is solved when I add ULL to the end of the integer. Thank you to everyone who helped on that.

On the overflow problem: I was literally trying to reverse the biggest possible number representable with 64 bits. Being such number 18446744073709551615 the reverse of it would have the same amount of digits but it would start with "516155...". This number is visibly bigger than the starting one and as such I was getting an overflow.

I guess I didn't think enough before posting Thank you anyway to everyone who helped!

0
John Bollinger On

One of the numbers for testing my function needs 64 bits to be stored and since I'm coding in C I need an unsigned long long to store such a number.

Well, signed or unsigned long long does need to be able to accommodate at least 64-bit values, so unsigned long long should suit. But if you specifically want exactly 64 bits then you should #include <stdint.h> and use type uint64_t. You can then also use the corresponding macro for forming a constant of that type:

uint64_t num = UINT64_C(18446744073709551615);

When I try to define a variabile with that data type and assign the number to it gcc will tell me "integer constant is so large that it is unsigned" even though I already specified it was.

No. That warning is about the constant itself, not about the variable. The type of the constant is determined from its form, not from the type of the variable you are initializing. The warning is harmless in this case, but if you want to avoid it then adding an appropriate type suffix to the constant will accomplish that, as you discovered.

Through the debugger I watched the process and the variabile reversed gets an overflow on the last cycle of the for, when it gets multiplied by 10.

That a given data type can represent a certain number N does not guarantee that it can also express the number whose decimal representation is the reverse of N's. If your unsigned long long has exactly 64 value bits, then the largest value it can represent, ULLONG_MAX, is 18446744073709551615, which also happens to be your test num. Observe that reversing the decimal digits of that number produces a larger number -- that number is not representable with only 64 bits, so yes, it is to be expected that your attempt to reverse will overflow.

It's unclear what your requirements are here. If you really need to do math with such large, high-precision numbers then you should probably engage an arbitrary-precision math library, such as GMP. But if all you want to do is reverse the decimal representation for display, then you could consider writing it to a decimal string (sprintf(buffer, "%llu", num)) and reversing the characters of the string.