What is the result of 'wrapping' a multiplication overflow? - c

The bcache source here contains the following line:
schedule_delayed_work(&dc->writeback_rate_update,
dc->writeback_rate_update_seconds * HZ);
writeback_rate_update_seconds is defined as unsigned int, which appears to be 32 bit on x86_64, and I am not sure what type HZ has but I believe the value is 1000 and assume it is 32-bit or less.
If I set writeback_rate_update_seconds to 2147483647, what value actually gets passed to schedule_delayed_work? The second parameter of schedule_delayed_work appears to be a long, but that won't mean the operands are promoted to long prior to the multiplication overflow, will it?

Given:
#include <stdio.h>
#include <stdlib.h>
int schedule_delayed_work( unsigned long param )
{
printf("value: %lu\n", param);
return 0;
}
int main(int argc, char **argv)
{
unsigned int writeback_rate_update_seconds;
unsigned int HZ;
writeback_rate_update_seconds = 2147483647;
HZ = 1000;
schedule_delayed_work( writeback_rate_update_seconds * HZ );
return 0;
}
You will get 4294966296 passed to the function.
If you change the function call to cast:
schedule_delayed_work( (unsigned long) writeback_rate_update_seconds * HZ );
... you will get 2147483647000 passed to the function.
I've not looked in the C standard to see what the standard behaviour is, but this was tested with:
Apple LLVM version 8.1.0 (clang-802.0.38)
Target: x86_64-apple-darwin16.7.0

If both operands fit in unsigned int (if HZ is constant 1000, it is of type int and fits in unsigned int) they're promoted to unsigned int. With unsigned integers the overflow is well-defined; the resulting value is the value of calculation modulo (UINT_MAX plus one). That is, the maximum result is UINT_MAX; UINT_MAX + 1 will result in 0, UINT_MAX + 2 will result in 1 and so on.
The type of the receiver (here, the type of the argument that receives the result) doesn't matter at all. To avoid wraparounds, cast one of the arguments as a wider integer type (for example unsigned long is 64 bits in 64-bit Linux; or even better, use a fixed-width type such as uint64_t).

Related

How can I confirm the range of unsigned long integer in 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.

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

8051 16 bit addition/multiplication result 16 bit instead of 32 bit

I have a problem. In this program the variable x should be set to 0x10000 but in both operations the result is 0.
This is not the main program but a test to find the reason for the error. I am currently making a 64 bit multiplier with hex input. I used 16-bit multiplication using Keil and Proteus
int main() {
unsigned long int x = 0;
x = 0x8000 * 0x2;
x = 0x8000 + 0x8000;
return 0;
}
The literal 0x8000 is of type unsigned int. On your 16-bit machine, the int and therefore unsigned int is of the natural size of 16 bits, the smallest accepted by the C standard. The integer promotion rules say that the smaller width types are widened to int or unsigned int, but no further (C11 n1570 6.3.1.1p2):
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. 58) All other types are unchanged by the integer promotions.
An operand is widened from int or unsigned int only if the other operand has a greater rank.
Here, 0x8000 + 0x8000 is calculated using unsigned int which will wrap to 0, because the maximum value that can be represented in unsigned int is 0xFFFF.
You should force at least one of the operands to unsigned long using either the suffix UL, or by adding an explicit cast:
int main() {
unsigned long int x=0;
/* unsigned long int * int */
x = 0x8000UL * 0x2;
/* unsigned long + unsigned int */
x = (unsigned long)0x8000 + 0x8000;
return 0;
}
See also In a C expression where unsigned int and signed int are present, which type will be promoted to what type? for general discussion.
It is not clear way (or that) the variable x should be 0x10000. x is a unsigned long int, but the values you assign to it are unsigned int. And if on that platform int is only 16 bit, both 0x8000 * 2 and 0x8000 + 0x8000 are 0.
Try using 0x8000L (or better 0x8000UL) for creating long literals at the first place.

The modulo operation doesn't seem to work on a 64-bit value of all ones

So... the modulo operation doesn't seem to work on a 64-bit value of all ones.
Here is my C code to set up the edge case:
#include <stdio.h>
int main(int argc, char *argv[]) {
long long max_ll = 0xFFFFFFFFFFFFFFFF;
long long large_ll = 0x0FFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("\n64-bit numbers:\n");
printf("0x%016llX\n", max_ll % mask_ll);
printf("0x%016llX\n", large_ll % mask_ll);
long max_l = 0xFFFFFFFF;
long large_l = 0x0FFFFFFF;
long mask_l = 0x00000F00;
printf("\n32-bit numbers:\n");
printf("0x%08lX\n", max_l % mask_l);
printf("0x%08lX\n", large_l % mask_l);
return 0;
}
The output shows this:
64-bit numbers:
0xFFFFFFFFFFFFFFFF
0x000000FFFFFFFFFF
32-bit numbers:
0xFFFFFFFF
0x000000FF
What is going on here?
Why doesn't modulo work on a 64-bit value of all ones, but it will on a 32-bit value of all ones?
It this a bug with the Intel CPU? Or with C somehow? Or is it something else?
More Info
I'm on a Windows 10 machine with an Intel i5-4570S CPU. I used the cl compiler from Visual Studio 2015.
I also verified this result using the Windows Calculator app (Version 10.1601.49020.0) by going into the Programmer mode. If you try to modulus 0xFFFF FFFF FFFF FFFF with anything, it just returns itself.
Specifying unsigned vs signed didn't seem to make any difference.
Please enlighten me :) I actually did have a use case for this operation... so it's not purely academic.
Your program causes undefined behaviour by using the wrong format specifier.
%llX may only be used for unsigned long long. If you use the right specifier, %lld then the apparent mystery will go away:
#include <stdio.h>
int main(int argc, char* argv[])
{
long long max_ll = 0xFFFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("%lld %% %lld = %lld\n", max_ll, mask_ll, max_ll % mask_ll);
}
Output:
-1 % 16492674416640 = -1
In ISO C the definition of the % operator is such that (a/b)*b + a%b == a. Also, for negative numbers, / follows "truncation towards zero".
So -1 / 16492674416640 is 0, therefore -1 % 16492674416640 must be -1 to make the above formula work.
As discussed in comments, the following line:
long long max_ll = 0xFFFFFFFFFFFFFFFF;
causes implementation-defined behaviour (assuming that your system has long long as a 64-bit type). The constant 0xFFFFFFFFFFFFFFFF has type unsigned long long, and it is out of range for long long whose maximum permitted value is 0x7FFFFFFFFFFFFFFF.
When an out-of-range assignment is made to a signed type, the behaviour is implementation-defined, which means the compiler documentation must say what happens.
Typically, this will be defined as generating the value which is in range of long long and has the same representation as the unsigned long long constant has. In 2's complement , (long long)-1 has the same representation as the unsigned long long value 0xFFFFFFFFFFFFFFFF, which explains why you ended up with max_ll holding the value -1.
Actually it does make a difference whether the values are defined as signed or unsigned:
#include <stdio.h>
#include <limits.h>
int main(void) {
#if ULLONG_MAX == 0xFFFFFFFFFFFFFFFF
long long max_ll = 0xFFFFFFFFFFFFFFFF; // converts to -1LL
long long large_ll = 0x0FFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("\n" "signed 64-bit numbers:\n");
printf("0x%016llX\n", max_ll % mask_ll);
printf("0x%016llX\n", large_ll % mask_ll);
unsigned long long max_ull = 0xFFFFFFFFFFFFFFFF;
unsigned long long large_ull = 0x0FFFFFFFFFFFFFFF;
unsigned long long mask_ull = 0x00000F0000000000;
printf("\n" "unsigned 64-bit numbers:\n");
printf("0x%016llX\n", max_ull % mask_ull);
printf("0x%016llX\n", large_ull % mask_ull);
#endif
#if UINT_MAX == 0xFFFFFFFF
int max_l = 0xFFFFFFFF; // converts to -1;
int large_l = 0x0FFFFFFF;
int mask_l = 0x00000F00;
printf("\n" "signed 32-bit numbers:\n");
printf("0x%08X\n", max_l % mask_l);
printf("0x%08X\n", large_l % mask_l);
unsigned int max_ul = 0xFFFFFFFF;
unsigned int large_ul = 0x0FFFFFFF;
unsigned int mask_ul = 0x00000F00;
printf("\n" "unsigned 32-bit numbers:\n");
printf("0x%08X\n", max_ul % mask_ul);
printf("0x%08X\n", large_ul % mask_ul);
#endif
return 0;
}
Produces this output:
signed 64-bit numbers:
0xFFFFFFFFFFFFFFFF
0x000000FFFFFFFFFF
unsigned 64-bit numbers:
0x000000FFFFFFFFFF
0x000000FFFFFFFFFF
signed 32-bit numbers:
0xFFFFFFFF
0x000000FF
unsigned 32-bit numbers:
0x000000FF
0x000000FF
64 bit hex constant 0xFFFFFFFFFFFFFFFF has value -1 when stored into a long long. This is actually implementation defined because of out of range conversion into a signed type, but on Intel processors, with current compilers, the conversion just keeps the same bit pattern.
Note that you are not using the fixed size integers defined in <stdint.h>: int64_t, uint64_t, int32_t and uint32_t. long long types are specified in the standard as having at least 64 bits, and on Intel x86_64, they do, and long has at least 32 bits, but for the same processor, the size differs between environments: 32 bits in Windows 10 (even in 64 bit mode) and 64 bits on MaxOS/10 and linux64. This is the reason why you observe surprising behavior for the long case where unsigned and signed may produce the same result. They don't on Windows, but they do in linux and MacOS because the computation is done in 64 bits and these values are just positive numbers.
Also note that LLONG_MIN / -1 and LLONG_MIN % -1 both invoke undefined behavior because of signed arithmetic overflow, and this one is not ignored on Intel PCs, it usually fires an uncaught exception and exits the program, just like 1 / 0 and 1 % 0.
Try putting unsigned before your long long. As a signed number, your 0xFF...FF is actually -1 on most platforms.
Also, in your code, your 32-bit numbers are still 64-bits (you have them declared as long long as well).

Printing unsigned long long using %d

Why do I get -1 when I print the following?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
I know I should use llu instead of d, but why do I get -1 instead of 18446744073709551615LL?
Is it because of overflow?
In C (99), LLONG_MAX, the maximum value of long long int type is guaranteed to be at least 9223372036854775807. The maximum value of an unsigned long long int is guaranteed to be at least 18446744073709551615, which is 264−1 (0xffffffffffffffff).
So, initialization should be:
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
(Note the ULL.) Since largestIntegerInC is of type unsigned long long int, you should print it with the right format specifier, which is "%llu":
$ cat test.c
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
/* good */
printf("%llu\n", largestIntegerInC);
/* bad */
printf("%d\n", largestIntegerInC);
return 0;
}
$ gcc -std=c99 -pedantic test.c
test.c: In function ‘main’:
test.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long unsigned int’
The second printf() above is wrong, it can print anything. You are using "%d", which means printf() is expecting an int, but gets a unsigned long long int, which is (most likely) not the same size as int. The reason you are getting -1 as your output is due to (bad) luck, and the fact that on your machine, numbers are represented using two's complement representation.
To see how this can be bad, let's run the following program:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
const char *fmt;
unsigned long long int x = ULLONG_MAX;
unsigned long long int y = 42;
int i = -1;
if (argc != 2) {
fprintf(stderr, "Need format string\n");
return EXIT_FAILURE;
}
fmt = argv[1];
printf(fmt, x, y, i);
putchar('\n');
return 0;
}
On my Macbook, running the program with "%d %d %d" gives me -1 -1 42, and on a Linux machine, the same program with the same format gives me -1 42 -1. Oops.
In fact, if you are trying to store the largest unsigned long long int number in your largestIntegerInC variable, you should include limits.h and use ULLONG_MAX. Or you should store assing -1 to your variable:
#include <limits.h>
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = ULLONG_MAX;
unsigned long long int next = -1;
if (next == largestIntegerInC) puts("OK");
return 0;
}
In the above program, both largestIntegerInC and next contain the largest possible value for unsigned long long int type.
It's because you're passing a number with all the bits set to 1. When interpreted as a two's complement signed number, that works out to -1. In this case, it's probably only looking at 32 of those one bits instead of all 64, but that doesn't make any real difference.
In two's complement arithmetic, the signed value -1 is the same as the largest unsigned value.
Consider the bit patterns for negative numbers in two's complement (I'm using 8 bit integers, but the pattern applies regardless of the size):
0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD
So, you can see that negative 1 has the bit pattern of all 1's which is also the bit pattern for the largest unsigned value.
You used a format for a signed 32-bit number, so you got -1. printf() can't tell internally how big the number you passed in is, so it just pulls the first 32 bits from the varargs list and uses them as the value to be printed out. Since you gave a signed format, it prints it that way, and 0xffffffff is the two's complement representation of -1.
You can (should) see why in compiler warning. If not, try to set the highest warning level. With VS I've got this warning: warning C4245: 'initializing' : conversion from '__int64' to 'unsigned __int64', signed/unsigned mismatch.
No, there is no overflow. It's because it isn't printing the entire value:
18446744073709551615 is the same as 0xFFFFFFFFFFFFFFFF. When printf %d processes that, it grabs only 32 bits (or 64 bits if it's a 64-bit CPU) for conversion, and those are the signed value -1.
If the printf conversion had been %u instead, it would show either 4294967295 (32 bits) or 18446744073709551615 (64 bits).
An overflow is when a value increases to the point where it won't fit in the storage allocated. In this case, the value is allocated just fine, but isn't being completely retrieved.

Resources