C Манипулирование беззнаковыми интервалами - целочисленное переполнение

Я экспериментирую как с типами данных unsigned int, так и с параметрами основных методов в простых программах на C. В качестве эксперимента я написал программу, которая берет число int из командной строки в качестве параметра основного метода и суммирует каждое целое число между этим числом и 0.

Например. Программа вычисляет f (n) = (1 + 2 + 3 ... + n), допустимое, когда n> 0.

#include <stdio.h>
#include <stdlib.h>

const unsigned int MAX_NUM = 92681; //Max input that will avoid int overflow later on

unsigned int sum(unsigned int x); 

int main(int argc, char *argv[]) { 

    unsigned int input = atoi(argv[1]); 

    if (input < 0 || input > MAX_NUM) {
        printf("Invalid input! Input must be less than 92682\n");
        exit(0); //If input > MAX_NUM, quit program
    }

    unsigned int result = sum(input);

    printf("Sum to %d = %d\n", input, result);

    return 0;
}

unsigned int sum(unsigned int x) {
    unsigned int sum = 0;
    unsigned int y;
    for (y = 0; y <= x; y++) {
        sum += y;
        printf("Current sum:\t%u\n",sum);
    }
    return sum;
}

Первое, что я начал замечать, это целочисленное переполнение, когда f (n)> 2147483648 - то есть максимальное значение для подписанного int.

Я вручную вычислил максимальные значения, для которых результаты, сгенерированные моей программой, будут действительными (например, до переполнения целых чисел), равными 65535 для целых чисел со знаком и 92681 для целых чисел без знака.

Выполнение программы для подписанных целых чисел дало ожидаемые результаты - на 65535 очень большое положительное число превратилось в очень большое отрицательное число, поскольку целое число переполнилось.

Затем я прошел и изменил каждое «int» на «unsigned int». Несмотря на это целочисленное переполнение происходит, как если бы целые числа были подписаны, а не беззнаковые.

У меня вопрос а) Почему это? б) Как я могу сделать так, чтобы мой ответ мог использовать весь диапазон unsigned int, то есть от 0 до (2 ^ 32) - 1 (поскольку мне не нужны отрицательные значения!).

Большое спасибо!


person davidhood2    schedule 14.10.2014    source источник
comment
Совет от Гаусса: n * (n-1) / 2.   -  person Grzegorz Szpetkowski    schedule 15.10.2014
comment
Вот как я рассчитал максимальные значения, т.е. 2 ^ 32 = п * (п-1) / 2   -  person davidhood2    schedule 15.10.2014


Ответы (1)


Вы забыли изменить окончательный printf формат с подписанного на неподписанный.

Изменять:

printf("Sum to %d = %d\n", input, result);

to:

printf("Sum to %u = %u\n", input, result);
               ^^   ^^

Обратите внимание, что включение предупреждений компилятора (например, gcc -Wall ...) предупредило бы вас об этом. Всегда включайте предупреждения компилятора и всегда обращайте на них внимание.

person Paul R    schedule 14.10.2014
comment
Что ж, если это научит вас всегда включать предупреждения компилятора, то оно того стоит. ;-) - person Paul R; 15.10.2014
comment
@PaulR Не уверен, что использует OP, но я бы хотел, чтобы MSVC, наконец, начал выдавать предупреждения об этом ... /analyze вроде бы делает, но ужасно медленный и не предупреждает об этом конкретном случае (несоответствие подписи / беззнака). Ну хоть C99 (sn) printf в 14 версии наконец-то получим, вздох ...;) - person user2802841; 15.10.2014
comment
@ user2802841: Я не понимал, что MSVC все еще отстает в этом, хотя, думаю, я не должен удивляться - большинство других компиляторов, конечно, уже много лет генерируют предупреждения для printf и др. - person Paul R; 15.10.2014
comment
@ user2802841: Чтобы такие предупреждения printf были полезными, они должны учитывать, как были созданы аргументы. Если u относится к типу unsigned char, требование (unsigned) приведения в printf является бессмысленным педантизмом, особенно потому, что нет причин, по которым не тупая реализация не обрабатывала бы подписанные и неподписанные типы взаимозаменяемо при работе со значениями, которые находятся в пределах обоих диапазонов. - person supercat; 14.07.2016