Глиф Учебники

Переполнение int: отрицательный или странный ответ

Справочник · C · 2 минуты чтения

int хранит числа только от −2 147 483 648 до 2 147 483 647. Если результат больше, он не помещается, и ответ получается неверным — часто отрицательным. Решение — тип long long (до 9 223 372 036 854 775 807) и вычисление сразу в нём:

C
#include <stdio.h>

int main(void) {
    int a, b;
    scanf("%d %d", &a, &b);
    long long p = (long long)a * b;
    printf("%lld\n", p);
    return 0;
}
Ввод
100000 100000
Вывод
10000000000

Почему не хватает просто long long p = a * b

В выражении a * b оба числа int, поэтому и умножение выполняется в int — переполнение случается до записи в long long. Приведение (long long)a превращает первое число в long long, и тогда всё умножение идёт в нём. То же касается сложения в цикле: сумма должна быть long long с самого начала.

C
#include <stdio.h>

int main(void) {
    int a, b;
    scanf("%d %d", &a, &b);
    long long p = a * b;
    printf("%lld\n", p);
    return 0;
}

При вводе 100000 100000 результат не определён: по стандарту C переполнение знаковых целых — неопределённое поведение. Обычно выводится 1410065408 — «обрезанное» значение, но полагаться нельзя: компилятор при оптимизации может рассчитывать на то, что переполнения нет, и программа поведёт себя совсем странно.

Границы типов

Точные границы — в заголовке limits.h:

C
#include <stdio.h>
#include <limits.h>

int main(void) {
    printf("int: от %d до %d\n", INT_MIN, INT_MAX);
    printf("long long: до %lld\n", LLONG_MAX);
    printf("unsigned int: до %u\n", UINT_MAX);
    return 0;
}
Вывод
int: от -2147483648 до 2147483647
long long: до 9223372036854775807
unsigned int: до 4294967295

Ориентиры для задач:

ТипПределФормат
intоколо 2·10⁹%d
long longоколо 9·10¹⁸%lld
unsigned long longоколо 1,8·10¹⁹, только неотрицательные%llu

Числа до миллиарда помещаются в int, но их сумма или произведение — уже нет. Факториал: 12! ещё помещается в int, 13! — уже нет; в long long помещается до 20!.

Тип long не берите для больших чисел: в Windows он такой же, как int (4 байта), а в 64-битном Linux — 8 байт. long long везде не меньше 8 байт.

unsigned — без знака

Беззнаковые типы (unsigned int, unsigned long long) хранят только неотрицательные числа, зато вдвое больше. Их переполнение определено: счёт идёт по кругу, после наибольшего числа снова 0. Но с ними легко ошибиться: 0u - 1 (0u — ноль типа unsigned) даёт не −1, а 4294967295, и цикл for (unsigned i = n - 1; i >= 0; i--) не закончится никогда — условие i >= 0 для беззнакового числа верно всегда.

Как заметить переполнение