1. Giới thiệu
Thư viện Go Decimal là một công cụ mạnh mẽ để xử lý số thập phân có độ chính xác tuyệt đối trong ngôn ngữ Go. Nó cho phép thực hiện các phép cộng, trừ, nhân và chia mà không làm mất độ chính xác. Hơn nữa, nó cung cấp các chức năng như chuỗi ký tự/khôi phục từ cơ sở dữ liệu/SQL, cũng như chuỗi ký tự JSON/XML.
2. Cài đặt
Để cài đặt thư viện Go Decimal, bạn có thể sử dụng lệnh sau:
go get github.com/shopspring/decimal
Vui lòng lưu ý rằng thư viện Decimal yêu cầu phiên bản Go >=1.7.
3. Sử dụng cơ bản
Để sử dụng thư viện Decimal trong chương trình Go, nhập gói "github.com/shopspring/decimal". Dưới đây là một đoạn mã ví dụ đơn giản minh họa việc sử dụng cơ bản:
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. Tạo biến Decimal
Thư viện Decimal cung cấp các phương thức khác nhau để tạo biến Decimal. Dưới đây là các API được hỗ trợ:
-
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. Các phép toán số học
Thư viện Go Decimal cung cấp một số phép toán số học có thể được thực hiện trên biến Decimal. Dưới đây là một số phép toán được hỗ trợ:
-
Add(d2 Decimal) Decimal
: Cộng hai giá trị Decimal và trả về kết quả. -
Sub(d2 Decimal) Decimal
: Trừ một giá trị Decimal từ một giá trị khác và trả về kết quả. -
Div(d2 Decimal) Decimal
: Chia một giá trị Decimal cho một giá trị khác và trả về kết quả. -
DivRound(d2 Decimal, precision int32) Decimal
: Chia một giá trị Decimal cho một giá trị khác và trả về kết quả với độ chính xác được chỉ định. -
Mod(d2 Decimal) Decimal
: Tính toán số dư (phần còn lại) của một giá trị Decimal được chia cho một giá trị khác và trả về kết quả. -
Mul(d2 Decimal) Decimal
: Nhân hai giá trị Decimal và trả về kết quả.
Bạn có thể sử dụng những phép toán này để thực hiện các phép tính số học thông thường trên các giá trị Decimal. Dưới đây là một ví dụ minh họa việc sử dụng những phép toán này:
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
Trong ví dụ trên, chúng ta sử dụng phương thức Mul()
để tính toán subtotal
bằng cách nhân price
và quantity
. Sau đó, chúng ta tính toán tax
bằng cách nhân subtotal
với tỷ lệ thuế. Cuối cùng, chúng ta tính toán total
bằng cách thêm subtotal
và tax
sử dụng phương thức Add()
.
6. Các Phép Làm Tròn
Thư viện Go Decimal cung cấp một số phép làm tròn có thể được sử dụng để làm tròn giá trị Decimal đến một độ chính xác cụ thể. Dưới đây là một số phép làm tròn có sẵn:
-
Round(places int32) Decimal
: Làm tròn số Decimal đến số chữ số thập phân cụ thể. -
RoundBank(places int32) Decimal
: Làm tròn số Decimal sử dụng phép làm tròn theo phương pháp ngân hàng đến số chữ số thập phân cụ thể. -
RoundCash(interval uint8) Decimal
: Làm tròn số Decimal đến các khoảng cụ thể, chẳng hạn như 5 cents, 10 cents, 25 cents, 50 cents hoặc 1 đô la. -
RoundCeil(places int32) Decimal
: Làm tròn số Decimal lên gần với vô cực dương. -
RoundDown(places int32) Decimal
: Làm tròn số Decimal xuống gần với số 0. -
RoundFloor(places int32) Decimal
: Làm tròn số Decimal xuống gần với vô cực âm. -
RoundUp(places int32) Decimal
: Làm tròn số Decimal đi rời xa khỏi số 0.
6.1. Làm Tròn
Phương thức Round làm tròn số Decimal đến số chữ số thập phân cụ thể. Nếu places < 0, nó sẽ làm tròn phần nguyên đến gần với 10^(-places) gần nhất.
NewFromFloat(5.45).Round(1).String() // Kết quả: "5.5"
NewFromFloat(545).Round(-1).String() // Kết quả: "550"
6.2. Làm Tròn theo Phương Pháp Ngân Hàng
Phương thức RoundBank làm tròn số Decimal đến places chữ số thập phân cụ thể. Nếu khoảng cách giữa chữ số cuối cùng cần được làm tròn và hai số nguyên gần nhất bằng nhau, giá trị sau khi làm tròn sẽ lấy giá trị chẵn.
Nếu places < 0, phần nguyên sẽ được làm tròn gần với 10^(-places) gần nhất.
NewFromFloat(5.45).RoundBank(1).String() // Kết quả: "5.4"
NewFromFloat(545).RoundBank(-1).String() // Kết quả: "540"
NewFromFloat(5.46).RoundBank(1).String() // Kết quả: "5.5"
NewFromFloat(546).RoundBank(-1).String() // Kết quả: "550"
NewFromFloat(5.55).RoundBank(1).String() // Kết quả: "5.6"
NewFromFloat(555).RoundBank(-1).String() // Kết quả: "560"
6.3. Làm Tròn theo Phương Pháp Tiền Mặt
Phương thức RoundCash (còn được gọi là làm tròn tiền mặt/xu/Irish) làm tròn số Decimal đến các khoảng cụ thể. Số tiền thanh toán của giao dịch tiền mặt sẽ được làm tròn đến bội số gần nhất của đơn vị tiền tệ nhỏ nhất. Các khoảng cụ thể có sẵn bao gồm: 5, 10, 25, 50 và 100; bất kỳ con số nào khác sẽ dẫn đến một ngoại lệ.
5: Làm tròn 5 cents 3.43 => 3.45
10: Làm tròn 10 cents 3.45 => 3.50 (5 được làm tròn lên)
25: Làm tròn 25 cents 3.41 => 3.50
50: Làm tròn 50 cents 3.75 => 4.00
100: Làm tròn 100 cents 3.50 => 4.00
6.4. Làm Tròn Lên Gần Với Vô Cực Dương
Phương thức RoundCeil làm tròn số Decimal lên gần với vô cực dương.
NewFromFloat(545).RoundCeil(-2).String() // Kết quả: "600"
NewFromFloat(500).RoundCeil(-2).String() // Kết quả: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // Kết quả: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // Kết quả: "-1.5"
6.5. Làm Tròn Xuống Gần Với Số 0
Phương thức RoundDown làm tròn số Decimal xuống gần với số 0.
NewFromFloat(545).RoundDown(-2).String() // Kết quả: "500"
NewFromFloat(-500).RoundDown(-2).String() // Kết quả: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // Kết quả: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // Kết quả: "-1.5"
6.6. RoundFloor
RoundFloor làm tròn số thập phân về âm vô cùng.
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 làm tròn số thập phân ra xa khỏi số 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. Chuyển đổi kiểu Decimal sang chuỗi
Thư viện Decimal của Go cung cấp các phương thức để chuyển đổi các giá trị Decimal sang chuỗi. Dưới đây là một số phương thức có sẵn:
-
String(): string
: Trả về biểu diễn dạng chuỗi của số thập phân với một dấu thập phân cố định. -
StringFixed(places int32) string
: Trả về biểu diễn dạng chuỗi đã làm tròn với số vị trí thập phân cụ thể. -
StringFixedBank(places int32) string
: Trả về biểu diễn dạng chuỗi đã làm tròn (làm tròn theo phương pháp ngân hàng) với số vị trí thập phân cụ thể.
Bạn có thể chọn phương thức phù hợp theo nhu cầu của mình. Dưới đây là một ví dụ về việc chuyển đổi một số thập phân sang chuỗi:
d := decimal.NewFromFloat(5.45)
str := d.String()
fmt.Println("Biểu diễn chuỗi của số thập phân:", str) // Biểu diễn chuỗi của số thập phân: 5.45
// Ví dụ về 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"
// Ví dụ về 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. Câu hỏi phổ biến
Q: Tại sao không sử dụng trực tiếp float64? A: float64 không thể biểu diễn chính xác những con số như 0.1, điều này có thể dẫn đến những sai số nhỏ. Trong các tình huống liên quan đến tính toán tài chính, những sai số này có thể tích luỹ theo thời gian và gây ra những vấn đề đáng kể.
Q: Tại sao không sử dụng big.Rat trực tiếp? A: Mặc dù big.Rat có thể biểu diễn số hợp lý, nhưng không phù hợp để biểu diễn tiền tệ. Số thập phân thích hợp hơn cho các tính toán tài chính vì chúng có thể biểu diễn chính xác các phân số thập phân mà không mất đi sự chính xác.
Q: Tại sao API không tương tự như big.Int? A: API của thư viện Decimal ưu tiên tính khả dụng và đúng đắn hơn là hiệu suất. Trong khi API của big.Int giảm thiểu việc cấp phát bộ nhớ vì lý do hiệu suất, điều này có thể dẫn đến mã phức tạp và dễ gây lỗi. API của thư viện Decimal được thiết kế để đơn giản và dễ hiểu.