Что такое…

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

Иллюстрация: механический счётчик с колёсиками, который докрутился до конца и перескочил обратно на ноль

В декабре 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-м половина экрана превращается в кашу из символов, и пройти его невозможно.

Разные десятилетия, разные машины — одна и та же ошибка.

Как защититься

  1. Выбирай тип с запасом. Для id, счётчиков и денег в копейках — 64 бита (bigint).
  2. Не используй побитовые трюки для округления. Math.trunc, Math.floor — вместо | 0.
  3. Знай поведение своего языка. Python не переполняется вовсе: целые числа растут, сколько хватит памяти. Rust в отладочной сборке падает с ошибкой, а в релизной — молча «оборачивает». Java и C# по умолчанию оборачивают без предупреждения.
  4. Попроси ИИ проверить схему. Промпт: «Найди в моей схеме колонки integer, которые могут превысить 2 миллиарда, и предложи миграцию на bigint».

Переполнение — это то же, что потеря точности?

Нет. Переполнение — число перескочило через край и стало другим. Потеря точности — число осталось примерно правильным, но последние цифры округлились. В JavaScript чаще встретишь второе, а в базах и низкоуровневом коде — первое.

Почему именно 2 147 483 647?

Это 2³¹ − 1. Из 32 бит один уходит на знак плюс или минус. Остальные 31 дают чуть больше двух миллиардов вариантов.

Может ли переполниться дата?

Да. Время в Unix считают секундами с 1970 года. В 32-битной ячейке эти секунды закончатся 19 января 2038 года. Это и называют «проблемой 2038».

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →