Переполнение int: отрицательный или странный ответ
int хранит числа только от −2 147 483 648 до 2 147 483 647. Если результат больше, он не помещается, и ответ получается неверным — часто отрицательным. Решение — тип long long (до 9 223 372 036 854 775 807) и вычисление сразу в нём:
#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 10000010000000000Почему не хватает просто long long p = a * b
В выражении a * b оба числа int, поэтому и умножение выполняется в int — переполнение случается до записи в long long. Приведение (long long)a превращает первое число в long long, и тогда всё умножение идёт в нём. То же касается сложения в цикле: сумма должна быть long long с самого начала.
#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:
#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 для беззнакового числа верно всегда.
Как заметить переполнение
- Ответ отрицательный там, где должен быть положительным, или на больших тестах «странный», а на маленьких верный.
- Прикиньте наибольший результат по условию задачи: если он больше 2·10⁹ — нужен
long long. - Санитайзер (проверка, которую компилятор встраивает в программу) ловит переполнение при работе: соберите с
-fsanitize=undefined, и программа напишетsigned integer overflowс номером строки. - Константы gcc проверяет сам: на
int x = 2147483647 + 1;он предупреждаетinteger overflow in expression.