Ответ
Для работы с деньгами следует избегать типов с плавающей запятой (float32, float64) из-за их неточности в представлении десятичных дробей. Лучшими практиками являются:
1. int (в минимальных единицах)
Хранить сумму в минимальных денежных единицах (копейках, центах). Это самый простой, быстрый и распространенный подход.
- Преимущества: Высокая производительность, простота, отсутствие проблем с точностью, возможность использовать в качестве ключа
map. - Недостатки: Требуется ручное преобразование при отображении пользователю (деление на 100).
// 123 рубля 45 копеек хранятся как 12345
priceInKopecks := 12345
// Для отображения
fmt.Printf("Цена: %.2f руб.n", float64(priceInKopecks)/100.0)2. Специализированные пакеты (decimal)
Использовать библиотеки для работы с десятичной арифметикой с фиксированной точностью. Самая популярная — github.com/shopspring/decimal.
- Преимущества: Гарантированная точность, удобные методы для арифметических операций (сложение, вычитание, округление).
- Недостатки: Ниже производительность по сравнению с
int, добавляет внешнюю зависимость, экземплярыdecimal.Decimalне являются сравниваемыми и не могут быть ключами вmap.
import "github.com/shopspring/decimal"
price1, _ := decimal.NewFromString("100.50")
price2 := decimal.NewFromFloat(0.2)
// Точное сложение
total := price1.Add(price2) // "100.7"Почему float — плохой выбор?
Типы float хранят числа в двоичном формате, который не может точно представить большинство десятичных дробей. Это приводит к ошибкам округления.
// Классический пример
fmt.Println(0.1 + 0.2) // Выведет: 0.30000000000000004
// При работе с деньгами это недопустимо
var price float64 = 100.50
var tax float64 = 0.21
fmt.Println(price * tax) // Выведет: 21.105000000000004