user6854914
user6854914

Reputation:

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

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?

Upvotes: 9

Views: 943

Answers (2)

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).

Upvotes: 1

Phil
Phil

Reputation: 2422

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

Upvotes: 2

Related Questions