1. 소개
Go Decimal 라이브러리는 Go 언어에서 임의 정밀도의 고정 소수점(decimal)을 다루는 강력한 도구입니다. 정확도를 잃지 않고 덧셈, 뺄셈, 곱셈 및 나눗셈 연산이 가능합니다. 또한 데이터베이스/SQL 직렬화/역직렬화뿐만 아니라 JSON/XML 직렬화/역직렬화와 같은 기능을 제공합니다.
2. 설치
Go Decimal 라이브러리를 설치하려면 다음 명령을 사용할 수 있습니다:
go get github.com/shopspring/decimal
Decimal 라이브러리는 Go 버전 >=1.7이 필요합니다.
3. 기본 사용
Go 프로그램에서 Decimal 라이브러리를 사용하려면 "github.com/shopspring/decimal" 패키지를 import하세요. 기본 사용법을 보여주는 간단한 예제 코드는 다음과 같습니다:
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:", subtotal) // Subtotal: 408.06
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 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:", subtotal) // Subtotal: 408.06
fmt.Println("Tax:", tax) // Tax: 36.244985
fmt.Println("Total:", total) // 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
: 소수를 0 방향으로 반올림합니다. -
RoundFloor(places int32) Decimal
: 소수를 음의 무한대 방향으로 반올림합니다. -
RoundUp(places int32) Decimal
: 소수를 0에서 떨어진 방향으로 반올림합니다.
6.1. Round
Round는 소수를 지정된 소수 자릿수로 반올림합니다. 만약 places가 음수이면, 정수 부분을 가장 가까운 10^(-places)로 반올림합니다.
NewFromFloat(5.45).Round(1).String() // 결과: "5.5"
NewFromFloat(545).Round(-1).String() // 결과: "550"
6.2. RoundBank
RoundBank는 소수를 특정 소수 자릿수로 반올림합니다. 만약 가장 가까운 두 정수까지의 거리가 동일하면, 반올림된 값은 짝수가 됩니다.
만약 places가 음수이면, 정수 부분을 가장 가까운 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(또는 cash/penny/Irish rounding으로도 알려짐)는 특정 간격으로 소수를 반올림합니다. 현금 거래의 결제 금액은 가장 작은 통화 단위의 가장 가까운 배수로 반올림됩니다. 가능한 간격은: 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은 소수를 0 방향으로 반올림합니다.
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() // Output: "500"
NewFromFloat(-500).RoundFloor(-2).String() // Output: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // Output: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // Output: "-1.4"
6.7. RoundUp
RoundUp은 소수점을 0으로부터 멀어지는 방향으로 반올림합니다.
NewFromFloat(545).RoundUp(-2).String() // Output: "600"
NewFromFloat(500).RoundUp(-2).String() // Output: "500"
NewFromFloat(1.1001).RoundUp(2).String() // Output: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // Output: "-1.4"
7. 문자열로 Decimal 타입 변환
Go Decimal 라이브러리는 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) // Output: "0.00"
NewFromFloat(0).StringFixed(0) // Output: "0"
NewFromFloat(5.45).StringFixed(0) // Output: "5"
NewFromFloat(5.45).StringFixed(1) // Output: "5.5"
NewFromFloat(5.45).StringFixed(2) // Output: "5.45"
NewFromFloat(5.45).StringFixed(3) // Output: "5.450"
NewFromFloat(545).StringFixed(-1) // Output: "550"
// StringFixedBank 예시
NewFromFloat(0).StringFixedBank(2) // Output: "0.00"
NewFromFloat(0).StringFixedBank(0) // Output: "0"
NewFromFloat(5.45).StringFixedBank(0) // Output: "5"
NewFromFloat(5.45).StringFixedBank(1) // Output: "5.4"
NewFromFloat(5.45).StringFixedBank(2) // Output: "5.45"
NewFromFloat(5.45).StringFixedBank(3) // Output: "5.450"
NewFromFloat(545).StringFixedBank(-1) // Output: "540"
8. 자주 묻는 질문
Q: 왜 float64를 직접 사용하지 않나요? A: float64는 0.1과 같은 숫자를 정확하게 표현할 수 없어 작은 오차가 발생할 수 있습니다. 금융 계산과 관련된 상황에서는 이러한 오차가 시간이 지남에 따라 축적되어 심각한 문제를 야기할 수 있습니다.
Q: 왜 big.Rat을 직접 사용하지 않나요? A: big.Rat은 유리수를 표현할 수 있지만, 통화를 표현하기에 적합하지 않습니다. 소수는 정밀도를 잃지 않고 십진수 분수를 정확하게 표현할 수 있어 금융 계산에 더 적합합니다.
Q: 왜 API가 big.Int와 유사하지 않나요? A: Decimal 라이브러리의 API는 성능보다 사용성과 정확성을 우선시합니다. 반면 big.Int의 API는 성능을 위해 메모리 할당을 줄이지만, 복잡하고 오류가 발생하기 쉬운 코드를 야기할 수 있습니다. Decimal 라이브러리의 API는 간단하고 이해하기 쉽도록 설계되었습니다.