Как правильно хранить деньги в коде: без потерь, округлений и боли

Деньги в программировании выглядят как обычные числа. Но стоит использовать неправильный тип данных, и система начинает незаметно терять копейки, округлять суммы или выдавать странные результаты в расчётах. В небольшом проекте это может остаться незамеченным. В продакшене с платежами это превращается в проблему.

Разберёмся, как хранить валюту правильно и какие подходы действительно работают.

Деньги это не просто число

Любая сумма существует не сама по себе, а в связке с валютой. А у валюты есть характеристики, которые напрямую влияют на хранение данных:

  • код по стандарту ISO 4217;

  • количество знаков после запятой;

  • минимальная расчётная единица;

  • минимальная физическая единица наличности;

  • возможные изменения точности со временем.

Большинство валют используют два знака после запятой. Но есть исключения: валюты без дробной части, валюты с тремя знаками, а криптовалюты могут иметь 8, 12 и даже 18 знаков после запятой.

Вывод простой: хранить просто число без указания валюты это архитектурная ошибка.

Главные требования к хранению

Если система работает с деньгами, она должна соответствовать следующим требованиям:

  1. Хранить сумму вместе с валютой.

  2. Поддерживать достаточную точность.

  3. Учитывать минимальную расчётную единицу.

  4. Корректно работать при накоплениях и промежуточных вычислениях.

Особенно это важно, если вы рассчитываете комиссии, проценты, дробные начисления или агрегируете тысячи операций.

Подход №1: хранить в целых минорных единицах

Самый надёжный и проверенный способ хранить деньги как целое число в минимальных единицах.

Например:

  • 10 долларов → 1000 центов

  • 5 евро → 500 центов

В базе данных это обычное целое число.

Почему это хорошо

  • Нет округлений.

  • Нет потерь точности.

  • Арифметика предсказуемая.

Где начинаются сложности

  • Нужно всегда помнить про масштаб.

  • При выводе требуется преобразование обратно.

  • Если изменится точность валюты, придётся пересчитывать данные.

  • В интеграциях легко забыть, что число хранится в центах.

Если используется этот подход, важно либо жёстко зафиксировать масштаб, либо хранить его рядом с числом.

Для хранения лучше использовать большие целые типы. В SQL это может быть BIGINT или DECIMAL с нулевой дробной частью.

Подход №2: использовать Decimal / Numeric

Второй популярный вариант хранить сумму как десятичное число с фиксированной точностью.

Например: DECIMAL(18, 2).

Плюсы

  • Число выглядит привычно: 10.50.

  • Не нужно вручную переводить в центы.

  • Легче читать данные в базе.

  • Меньше риска ошибиться при интеграции.

На что обратить внимание

  • Нужно правильно выбрать precision и scale.

  • При смене требований к точности может потребоваться изменение структуры таблиц.

  • Нельзя путать Decimal с float.

Этот подход чаще выбирают в корпоративных системах и бухгалтерии, где важна читаемость данных.

Чего делать нельзя

Не используйте float и double

Числа с плавающей точкой работают в двоичной системе. Поэтому:

0.1 + 0.2 может не равняться 0.3.

Для финансов это недопустимо. Даже если ошибка проявляется на 10-м знаке, она может накопиться при массовых расчётах.

Осторожно с типом MONEY

В некоторых СУБД есть специальный тип MONEY. Он кажется удобным, но часто имеет ограничения по точности и масштабированию. В серьёзных проектах обычно предпочитают явный Decimal или целые значения.

А что насчёт сверхточных вычислений?

Иногда бизнес-логика требует идеальной точности при дробных операциях. Например:

взять треть от суммы, а потом умножить результат обратно.

Обычные десятичные числа могут дать не идеально точный результат из-за округления. В теории можно использовать рациональные числа. Хранить дробь как числитель и знаменатель. Это гарантирует абсолютную точность в промежуточных вычислениях.

Но на практике такой подход сложен, плохо поддерживается в базах данных и применяется редко.

Что выбрать на практике

Если обобщить:

  • Для большинства сервисов подходит Decimal.

  • Для высоконагруженных систем и платёжной инфраструктуры часто используют хранение в минорных единицах как целое число.

  • Float и double лучше исключить полностью.

  • Всегда хранить валюту рядом с суммой.

  • Закладывать запас точности для накоплений и комиссий.

Деньги требуют аккуратности. Ошибка в типе данных редко проявляется сразу, но почти всегда проявляется в самый неудобный момент.

Если вы проектируете финансовый модуль, лучше потратить час на правильную модель хранения, чем потом неделю искать, куда исчезли копейки.

Комментарии (0)

Войдите, чтобы оставить комментарий

Похожие статьи