Q:

Performance of int vs unsigned long long for prime number testing in C

Why does it take longer to do an operation on an unsigned long long than on an int?
I've done several tests using a function to find the nth prime number using exactly the same code, I don't need to provide the code, you could put it simply as: why does an int / 2 take less time than an unsigned long long / 2?
I know the / 2 implies a cast to unsigned long long.
Here is the code:
int isPrime(int number) {
    if (number < 2) return 0;
    if (number == 2) return 1;
    if (number == 3) return 1;
    if (number == 5) return 1;
    if (!(number % 2)) return 0;
    if (!(number % 3)) return 0;
    if (!(number % 5)) return 0;
    if (number < 7 * 7) return 1;
    int step[] = { 7, 11, 13, 17, 19, 23, 29, 31 };
    int sentry = (int)sqrt((double)number);
    for (int r = 0; r < sentry; r += 30)
        for (int i = 0; i < 8; ++i)
            if (!(number % (r + step[i])))
                return 0;
    return 1;
} 

A:

int and unsigned long long most likely have a different size. Arithmetic operations on integers of different sizes may be implemented more or less efficiently depending on the CPU architecture and the compiler settings.
Without seeing any code, it is very difficult to determine which operation is likely to cause the difference in performance that you describe.  Dividing by 2 may actually more efficient on an unsigned long long, where it can always be implemented as a single bitshift by 1 position to the right, than on an int where an adjustment is required if the value is not known to be positive.
Other operations may take much longer on 64 bit values than on 32 bit values, such as multiplications and divisions or modulo operations, which are probably the bottleneck in your implementation.
Your code uses many divisions and modulo operations, these are most costly on unsigned long long than on int values, especially for the last loop where the divisions are not compiled as multiplications.
Note that you can slightly improve efficiency by defining step this way:
static const int step[] = { 7, 11, 13, 17, 19, 23, 29, 31 };

Note also that is may not be very efficient to mix floating point operations and integer arithmetic, and it may actually be incorrect to stop at sqrt((double)number) if number is a large unsigned long long because of the loss of precision converting a 64 bit integer to a 53 bit floating point mantissa.
Here is an alternative with fewer tests:
typedef unsigned long long int num_t;

#define PBITS64  ((1<<2) | (1<<3) | (1<<5) | (1<<7) | (1<<11) | (1<<13) | \
                  (1ULL<<17) | (1ULL<<19) | (1ULL<<23) | (1ULL<<29) | \
                  (1ULL<<31) | (1ULL<<37) | (1ULL<<41) | (1ULL<<43) | \
                  (1ULL<<47) | (1ULL<<53) | (1ULL<<59) | (1ULL<<61)
#define REMBITS30  ((1<<1) | (1<<7) | (1<<11) | (1<<13) | \
                    (1UL<<17) | (1UL<<19) | (1UL<<23) | (1UL<<29))

int isPrime(num_t number) {
    /* test all numbers below 64 */
    if (number < 64) return (PBITS64 >> number) & 1;
    /* test all multiples of 2, 3 and 5 */
    if (!((REMBITS30 >> (number % 30)) & 1)) return 0;
    static unsigned int const step[] = { 7, 11, 13, 17, 19, 23, 29, 31 };
    for (num_t r = 0; r * r < number - 16; r += 30) {
        for (int i = 0; i < 8; ++i) {
            if (!(number % (r + step[i])))
                return 0;
        }
    }
    return 1;
}

This method is much faster for smaller numbers and multiples of 2 3 and 5, it works for all unsigned long long.

