1. Введение
Библиотека Go Decimal - это мощный инструмент для работы с десятичными числами произвольной точности в языке Go. С его помощью можно выполнять операции сложения, вычитания, умножения и деления без потери точности. Кроме того, она предоставляет функциональность, такую как сериализация/десериализация для баз данных/SQL, а также сериализация/десериализация JSON/XML.
2. Установка
Для установки библиотеки Go Decimal вы можете использовать следующую команду:
go get github.com/shopspring/decimal
Обратите внимание, что библиотека Decimal требует версию Go >=1.7.
3. Основное использование
Для использования библиотеки Decimal в программе на Go импортируйте пакет "github.com/shopspring/decimal". Вот простой пример кода, демонстрирующий основное использование:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
price, err := decimal.NewFromString("136.02")
if err != nil {
panic(err)
}
quantity := decimal.NewFromInt(3)
fee, _ := decimal.NewFromString(".035")
taxRate, _ := decimal.NewFromString(".08875")
subtotal := price.Mul(quantity)
preTax := subtotal.Mul(fee).Add(decimal.NewFromFloat(1))
total := preTax.Mul(taxRate).Add(decimal.NewFromFloat(1))
fmt.Println("Итого без налога:", subtotal) // Итого без налога: 408.06
fmt.Println("Перед налогом:", preTax) // Перед налогом: 422.3421
fmt.Println("Налоги:", total.Sub(preTax)) // Налоги: 37.482861375
fmt.Println("Итого:", total) // Итого: 459.824961375
fmt.Println("Налоговая ставка:", total.Sub(preTax).Div(preTax)) // Налоговая ставка: 0.08875
}
4. Создание переменных Decimal
Библиотека Decimal предоставляет различные методы для создания переменных Decimal. Вот поддерживаемые API:
-
decimal.NewFromBigInt(value *big.Int, exp int32) Decimal
-
decimal.NewFromFloat(value float64) Decimal
-
decimal.NewFromFloat32(value float32) Decimal
-
decimal.NewFromFloatWithExponent(value float64, exp int32) Decimal
-
decimal.NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error)
-
decimal.NewFromInt(value int64) Decimal
-
decimal.NewFromInt32(value int32) Decimal
-
decimal.NewFromString(value string) (Decimal, error)
-
decimal.RequireFromString(value string) Decimal
5. Арифметические операции
Библиотека Go Decimal предоставляет несколько арифметических операций, которые можно выполнять над переменными Decimal. Вот некоторые поддерживаемые операции:
-
Add(d2 Decimal) Decimal
: Складывает два значения Decimal и возвращает результат. -
Sub(d2 Decimal) Decimal
: Вычитает одно значение Decimal из другого и возвращает результат. -
Div(d2 Decimal) Decimal
: Делит одно значение Decimal на другое и возвращает результат. -
DivRound(d2 Decimal, precision int32) Decimal
: Делит одно значение Decimal на другое и возвращает результат с указанной точностью. -
Mod(d2 Decimal) Decimal
: Вычисляет остаток от деления одного значения Decimal на другое и возвращает результат. -
Mul(d2 Decimal) Decimal
: Умножает два значения Decimal и возвращает результат.
Вы можете использовать эти операции для выполнения обычных арифметических расчетов с Decimal. Вот пример, демонстрирующий использование этих операций:
price, _ := decimal.NewFromString("136.02")
quantity := decimal.NewFromInt(3)
subtotal := price.Mul(quantity)
tax := subtotal.Mul(decimal.NewFromFloat(0.08875))
total := subtotal.Add(tax)
fmt.Println("Итого без налога:", subtotal) // Итого без налога: 408.06
fmt.Println("Налог:", tax) // Налог: 36.244985
fmt.Println("Итого:", total) // Итого: 444.304985
6. Операции округления
Библиотека Go Decimal предоставляет несколько операций округления, которые могут использоваться для округления десятичных значений до указанной точности. Вот некоторые доступные операции округления:
-
Round(places int32) Decimal
: Округляет десятичное значение до указанного количества десятичных знаков. -
RoundBank(places int32) Decimal
: Округляет десятичное значение по правилам банковского округления до указанного количества десятичных знаков. -
RoundCash(interval uint8) Decimal
: Округляет десятичное значение до определенного интервала, такого как 5 центов, 10 центов, 25 центов, 50 центов или 1 доллар. -
RoundCeil(places int32) Decimal
: Округляет десятичное значение в сторону положительной бесконечности. -
RoundDown(places int32) Decimal
: Округляет десятичное значение в сторону нуля. -
RoundFloor(places int32) Decimal
: Округляет десятичное значение в сторону отрицательной бесконечности. -
RoundUp(places int32) Decimal
: Округляет десятичное значение в сторону отличную от нуля.
6.1. Round
Round округляет десятичное значение до указанного количества десятичных знаков. Если places < 0, то он округляет целую часть до ближайшего 10^(-places).
NewFromFloat(5.45).Round(1).String() // Вывод: "5.5"
NewFromFloat(545).Round(-1).String() // Вывод: "550"
6.2. RoundBank
RoundBank округляет десятичное значение до places десятичных знаков. Если расстояние между последней цифрой, которая должна быть округлена, и двумя ближайшими целыми числами одинаково, округленное значение принимает четное число.
Если places < 0, целая часть будет округлена до ближайшего 10^(-places).
NewFromFloat(5.45).RoundBank(1).String() // Вывод: "5.4"
NewFromFloat(545).RoundBank(-1).String() // Вывод: "540"
NewFromFloat(5.46).RoundBank(1).String() // Вывод: "5.5"
NewFromFloat(546).RoundBank(-1).String() // Вывод: "550"
NewFromFloat(5.55).RoundBank(1).String() // Вывод: "5.6"
NewFromFloat(555).RoundBank(-1).String() // Вывод: "560"
6.3. RoundCash
RoundCash (также известный как округление до копеек / центов) округляет десятичное значение до конкретных интервалов. Оплата наличными в ходе финансовой транзакции будет округлена до ближайшего кратного наименьшей денежной единицы. Доступные интервалы: 5, 10, 25, 50 и 100; любое другое число приведет к исключению.
5: Округление до 5 центов 3.43 => 3.45
10: Округление до 10 центов 3.45 => 3.50 (5 округляется вверх)
25: Округление до 25 центов 3.41 => 3.50
50: Округление до 50 центов 3.75 => 4.00
100: Округление до 100 центов 3.50 => 4.00
6.4. RoundCeil
RoundCeil округляет десятичное значение в сторону положительной бесконечности.
NewFromFloat(545).RoundCeil(-2).String() // Вывод: "600"
NewFromFloat(500).RoundCeil(-2).String() // Вывод: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // Вывод: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // Вывод: "-1.5"
6.5. RoundDown
RoundDown округляет десятичное значение в сторону нуля.
NewFromFloat(545).RoundDown(-2).String() // Вывод: "500"
NewFromFloat(-500).RoundDown(-2).String() // Вывод: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // Вывод: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // Вывод: "-1.5"
6.6. RoundFloor
RoundFloor округляет десятичное число в сторону отрицательной бесконечности.
NewFromFloat(545).RoundFloor(-2).String() // Вывод: "500"
NewFromFloat(-500).RoundFloor(-2).String() // Вывод: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // Вывод: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // Вывод: "-1.4"
6.7. RoundUp
RoundUp округляет десятичное число от нуля.
NewFromFloat(545).RoundUp(-2).String() // Вывод: "600"
NewFromFloat(500).RoundUp(-2).String() // Вывод: "500"
NewFromFloat(1.1001).RoundUp(2).String() // Вывод: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // Вывод: "-1.4"
7. Преобразование десятичного типа в строку
Библиотека Go Decimal предоставляет методы для преобразования десятичных значений в строковое представление. Вот некоторые доступные методы:
-
String(): string
: Возвращает строковое представление десятичного числа с фиксированной десятичной точкой. -
StringFixed(places int32) string
: Возвращает округленное строковое представление с указанным количеством десятичных знаков. -
StringFixedBank(places int32) string
: Возвращает округленное (банковское округление) строковое представление с указанным количеством десятичных знаков.
Вы можете выбрать подходящий метод в соответствии с вашими потребностями. Ниже приведен пример преобразования десятичного числа в строку:
d := decimal.NewFromFloat(5.45)
str := d.String()
fmt.Println("Строковое представление десятичного числа:", str) // Строковое представление десятичного числа: 5.45
// Пример StringFixed
NewFromFloat(0).StringFixed(2) // Вывод: "0.00"
NewFromFloat(0).StringFixed(0) // Вывод: "0"
NewFromFloat(5.45).StringFixed(0) // Вывод: "5"
NewFromFloat(5.45).StringFixed(1) // Вывод: "5.5"
NewFromFloat(5.45).StringFixed(2) // Вывод: "5.45"
NewFromFloat(5.45).StringFixed(3) // Вывод: "5.450"
NewFromFloat(545).StringFixed(-1) // Вывод: "550"
// Пример StringFixedBank
NewFromFloat(0).StringFixedBank(2) // Вывод: "0.00"
NewFromFloat(0).StringFixedBank(0) // Вывод: "0"
NewFromFloat(5.45).StringFixedBank(0) // Вывод: "5"
NewFromFloat(5.45).StringFixedBank(1) // Вывод: "5.4"
NewFromFloat(5.45).StringFixedBank(2) // Вывод: "5.45"
NewFromFloat(5.45).StringFixedBank(3) // Вывод: "5.450"
NewFromFloat(545).StringFixedBank(-1) // Вывод: "540"
8. Общие вопросы
Q: Почему нельзя использовать float64 напрямую? A: float64 не может точно представлять числа, такие как 0.1, что может привести к небольшим ошибкам. В ситуациях, связанных с финансовыми расчетами, эти ошибки могут накапливаться со временем и вызывать значительные проблемы.
Q: Почему нельзя использовать big.Rat напрямую? A: Хотя big.Rat может представлять рациональные числа, он не подходит для представления валюты. Для финансовых расчетов лучше использовать десятичные числа, так как они могут точно представлять десятичные дроби без потери точности.
Q: Почему API не похож на big.Int? A: API библиотеки Decimal отдает предпочтение удобству использования и корректности перед производительностью. В то время как API big.Int уменьшает выделение памяти в целях повышения производительности, это может привести к сложному и подверженному ошибкам коду. API библиотеки Decimal разработан так, чтобы быть простым и понятным.