Что такое переполнение целого числа — и почему счётчик вдруг стал отрицательным

В декабре 2014 года YouTube поменял тип счётчика просмотров. Причина — клип «Gangnam Style». Его посмотрели больше 2 147 483 647 раз. А это ровно потолок числа, под который счётчик был рассчитан изначально.
Сам YouTube тогда пошутил, что не ждал таких цифр. Но за шуткой — настоящая инженерная ловушка. Называется переполнение целого числа. И её можно поймать в своём проекте задолго до миллиарда просмотров.
Что такое переполнение
Компьютер хранит целое число в ячейке фиксированного размера. Например, 32 бита. В такую ячейку влезает число от −2 147 483 648 до 2 147 483 647. Больше — нельзя.
Переполнение — это когда результат не влезает в ячейку. Представь механический счётчик пробега на четыре цифры. Докрутился до 9999, прибавил километр — и снова 0000. Машина не стала новой. Просто колёсикам некуда крутиться дальше.
С числами со знаком ещё веселее. Прибавь единицу к 2 147 483 647 — и в 32-битной ячейке получится −2 147 483 648. Самое большое число превращается в самое маленькое.
Как это выглядит в коде
В JavaScript обычные числа так не ломаются. Они дробные, 64-битные, и вместо переполнения просто теряют точность на очень больших значениях. Но 32-битная арифметика прячется в неожиданных местах:
2147483647 + 1 // 2147483648 — всё нормально
(2147483647 + 1) | 0 // -2147483648 — вот оно
2 ** 32 | 0 // 0
Побитовые операции (|, &, <<) всегда работают с 32-битными числами. Трюк x | 0, чтобы «отрезать дробь», любят вставлять и люди, и ИИ. На числах больше двух миллиардов он молча выдаёт мусор. Надёжнее Math.trunc(x).
Ещё одно место — типизированные массивы. С ними работают картинки, звук, файлы:
new Uint8Array([256, 300]) // [0, 44] — перескочило через 255
new Uint8ClampedArray([300, -5]) // [255, 0] — упёрлось в границы
Первый массив «оборачивается» по кругу, второй — упирается в потолок. Поэтому пиксели в <canvas> хранятся во втором: пересвеченный пиксель должен стать белым, а не чёрным.
Почему это важно тебе
Главная ловушка для новичка — база данных. Колонка типа integer в PostgreSQL — это те же 32 бита. Если id у таблицы событий, логов или сообщений объявлен как integer, однажды ты упрёшься в 2 147 483 647. Дальше любая вставка падает с ошибкой. Приложение перестаёт записывать данные — внезапно и целиком.
Звучит как много. Но таблица аналитики на 100 событий в секунду дойдёт до потолка примерно за восемь месяцев.
Поэтому для id бери bigint (64 бита, до 9,2 квинтиллиона). В Supabase так и сделано по умолчанию — int8. Если базу за тебя генерировал ИИ, проверь: serial и integer для быстрорастущих таблиц — плохой выбор. Поменять тип потом можно через миграцию, но на большой таблице это долго и болезненно.
Где переполнение уже ломало мир
- Ракета Ariane 5, 1996. Через ~37 секунд после старта бортовой компьютер переводил скорость из 64-битного дробного числа в 16-битное целое. Число не влезло. Ракета сошла с курса и самоуничтожилась.
- Boeing 787, 2015. Регулятор FAA обязал перезагружать самолёт хотя бы раз в 248 дней. Внутренний счётчик сотых долей секунды переполнялся ровно через этот срок — и генераторы могли отключиться.
- Pac-Man, уровень 256. Номер уровня хранился в 8 битах. На 256-м половина экрана превращается в кашу из символов, и пройти его невозможно.
Разные десятилетия, разные машины — одна и та же ошибка.
Как защититься
- Выбирай тип с запасом. Для id, счётчиков и денег в копейках — 64 бита (
bigint). - Не используй побитовые трюки для округления.
Math.trunc,Math.floor— вместо| 0. - Знай поведение своего языка. Python не переполняется вовсе: целые числа растут, сколько хватит памяти. Rust в отладочной сборке падает с ошибкой, а в релизной — молча «оборачивает». Java и C# по умолчанию оборачивают без предупреждения.
- Попроси ИИ проверить схему. Промпт: «Найди в моей схеме колонки
integer, которые могут превысить 2 миллиарда, и предложи миграцию наbigint».
Переполнение — это то же, что потеря точности?
Нет. Переполнение — число перескочило через край и стало другим. Потеря точности — число осталось примерно правильным, но последние цифры округлились. В JavaScript чаще встретишь второе, а в базах и низкоуровневом коде — первое.
Почему именно 2 147 483 647?
Это 2³¹ − 1. Из 32 бит один уходит на знак плюс или минус. Остальные 31 дают чуть больше двух миллиардов вариантов.
Может ли переполниться дата?
Да. Время в Unix считают секундами с 1970 года. В 32-битной ячейке эти секунды закончатся 19 января 2038 года. Это и называют «проблемой 2038».
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





