Деньги: bigint, numeric и валюта
Сумма хранится вместе с валютой и единицей измерения. Для расчётов используйте целые минимальные единицы или точный десятичный тип.
Плохо
CREATE TABLE price (amount double precision);
-- 123.45: неизвестны валюта и политика округления
Хорошо
CREATE TABLE price (
amount_minor bigint NOT NULL CHECK (amount_minor >= 0),
currency text NOT NULL CHECK (currency = 'RUB')
);
INSERT INTO price VALUES (12345, 'RUB'); -- 123 руб. 45 коп.
Почему это важно
float, real и double precision не гарантируют точное представление десятичных денежных дробей. Проверка в БД удерживает формат даже при записи другим сервисом.
Когда применять
PostgreSQL 18. Тип выбирается по единице измерения и максимальной сумме:
integer/int: целые минимальные единицы в доказанном диапазоне. Максимум 2 147 483 647, то есть 21 474 836,47 RUB при хранении копеек.bigint: целые минимальные единицы, если диапазонаintegerнедостаточно. У него тоже есть предел; это не бесконечный тип.numeric(p,s)/decimal(p,s): точные десятичные суммы, дробные тарифы и курсы.numeric(20,6)допускает 14 целых и 6 дробных разрядов. Точность и округление задаёт предметная область.
В примере поддерживается только RUB. Для нескольких валют нужна проверяемая таблица валют и их разрядности: не у всех валют две цифры после запятой; API провайдера может иметь исключения. Отрицательные суммы разрешайте для проводок, если это требует модель. PostgreSQL money зависит от денежных настроек локали, поэтому для переносимого контракта предпочтительнее явно заданные единицы.
В JavaScript Number гарантирует точность целых только в безопасном диапазоне от −(2^53 − 1) до 2^53 − 1. Большие bigint передавайте в JSON строкой; преобразование и арифметику выполняйте без промежуточного float.
Кто что делает
Человек
Определяет валюты, максимальные суммы, точность тарифов и правила округления/возвратов.
AI-агент
Готовит поля БД, ограничения и сериализацию. Проверяет точность на граничных суммах и сверяет единицы платёжного API.
Пример применения
Магазин принимает RUB: цена 1999,90 ₽ хранится как 199990 в amount_minor bigint. Комиссия рассчитывается десятичной арифметикой, округляется по согласованному правилу до копеек и только затем сохраняется. Для бухгалтерских проводок создаётся отдельная модель с допустимым отрицательным знаком.
Как проверить у себя
- 12345 минимальных единиц RUB преобразуются в 123,45 ₽ и обратно без потери копеек.
- Проверены предел типа, отрицательные значения, дробное деление и округление.
- В API описаны валюта, единица и строковый формат больших целых.
Первоисточники
Дата проверки отражает содержательную сверку. Синхронизация сайта не обновляет её автоматически.