1. การแนะนำ
ไลบรารี Go Decimal เป็นเครื่องมือที่มีประสิทธิภาพในการจัดการทศนิยมที่มีความแม่นยำแบบคงที่ในภาษา Go โดยมันช่วยในการบวก ลบ คูณ และหารโดยไม่สูญเสียความแม่นยำ นอกจากนี้ยังมีความสามารถในการจัดการการแปลงข้อมูลเช่น ซีเรียลไลซ์ / ดีซีเรียลไซเรียลเลชัน, รวมทั้ง ซีเรียลสลับ / ดีซียร์แอ็กซ์เอลไชเรียลเลชัน JSON/XML
2. การติดตั้ง
เพื่อที่จะติดตั้งไลบรารี Go Decimal คุณสามารถใช้คำสั่งต่อไปนี้:
go get github.com/shopspring/decimal
โปรดทราบว่าไลบรารี Decimal ต้องการ Go version >=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:", 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 มีเมทอดต่าง ๆ เพื่อสร้างตัวแปรทศนิยม ต่อไปนี้คือ 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 มีการดำเนินการทางคณิตศาสตร์หลายอย่างที่สามารถกระทำกับตัวแปรทศนิยม ต่อไปนี้คือการดำเนินการที่รองรับ:
-
Add(d2 Decimal) Decimal
: บวกค่าทศนิยมสองตัวและคืนผลลัพธ์ -
Sub(d2 Decimal) Decimal
: ลบค่าทศนิยมหนึ่งจากอีกตัวหนึ่งและคืนค่า -
Div(d2 Decimal) Decimal
: หารค่าทศนิยมหนึ่งด้วยอีกตัวและคืนค่า -
DivRound(d2 Decimal, precision int32) Decimal
: หารค่าทศนิยมหนึ่งด้วยอีกตัวและคืนค่าด้วยความแม่นยำที่ระบุ -
Mod(d2 Decimal) Decimal
: คำนวณเศษ (เศษ) ของค่าทศนิยมหนึ่งที่ถูกหารด้วยอีกตัวและคืนค่า -
Mul(d2 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
ณตัวอย่างข้างต้น เราใช้เมทอด Mul()
เพื่อคำนวณ subtotal
โดยการคูณ price
และ quantity
จากนั้นเราคำนวณ tax
โดยการคูณ subtotal
ด้วยอัตราภาษี สุดท้ายเราคำนวณ total
โดยการบวก subtotal
และ tax
โดยใช้เมทอด Add()
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 ที่ใกล้ที่สุดของ 10^(-places)
NewFromFloat(5.45).Round(1).String() // Output: "5.5"
NewFromFloat(545).Round(-1).String() // Output: "550"
6.2. การปัดตามหลักของธนาคาร (RoundBank)
RoundBank จะปัดเศษทศนิยมไปยังจำนวนทศนิยมที่กำหนด หากระยะห่างระหว่างหลักสุดท้ายที่จะปัดและจำนวนเต็มที่ใกล้ที่สุดเท่ากัน ค่าที่ปัดจะถือเป็นคู่
หาก places < 0 หน่วยจำนวนเต็มจะถูกปัดไปยัง 10^(-places) ที่ใกล้ที่สุด
NewFromFloat(5.45).RoundBank(1).String() // Output: "5.4"
NewFromFloat(545).RoundBank(-1).String() // Output: "540"
NewFromFloat(5.46).RoundBank(1).String() // Output: "5.5"
NewFromFloat(546).RoundBank(-1).String() // Output: "550"
NewFromFloat(5.55).RoundBank(1).String() // Output: "5.6"
NewFromFloat(555).RoundBank(-1).String() // Output: "560"
6.3. การปัดตามการทำธุรกรรมเงินสด (RoundCash)
RoundCash (หรือรูปแบบการปัดเงิน/เศษเงิน/การปัดแบบไอริช) จะปัดเศษทศนิยมไปยังระยะเวลาที่ระบุ จำนวนเงินที่ต้องจ่ายในธุรกรรมเงินสดจะถูกปัดไปยังตัวคูณที่ใกล้ที่สุดของหน่วยเงินที่ขนานน้อยที่สุด ระยะเวลาที่ใช้งานได้ คือ: 5, 10, 25, 50 และ 100 สตางค์ หมายเลขอื่น ๆ จะทำให้เกิดข้อยกเว้น
5: 5 cents rounding 3.43 => 3.45
10: 10 cents rounding 3.45 => 3.50 (5 is rounded up)
25: 25 cents rounding 3.41 => 3.50
50: 50 cents rounding 3.75 => 4.00
100: 100 cents rounding 3.50 => 4.00
6.4. การปัดทางบวก (RoundCeil)
RoundCeil จะปัดเศษทศนิยมไปทางบวก
NewFromFloat(545).RoundCeil(-2).String() // Output: "600"
NewFromFloat(500).RoundCeil(-2).String() // Output: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // Output: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // Output: "-1.5"
6.5. การปัดทางศูนย์ (RoundDown)
RoundDown จะปัดเศษทศนิยมไปทางศูนย์
NewFromFloat(545).RoundDown(-2).String() // Output: "500"
NewFromFloat(-500).RoundDown(-2).String() // Output: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // Output: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // Output: "-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. แปลงประเภททศนิยมเป็นสตริง
ไลบรารี่ก็จะทำการแปลงค่าทศนิยมเป็นรูปแบบสตริงได้ ซึ่งมีเมธอดต่าง ๆ ให้เลือกใช้ดังนี้:
-
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. คำถามที่พบบ่อย
ค: ทำไมไม่ใช้ float64 โดยตรง? ก: float64 ไม่สามารถแทนเลขอย่างถูกต้องสำหรับเลขเช่น 0.1 ซึ่งอาจทำให้เกิดความผิดพลาดเล็ก ๆ น้อย ๆ ในการคำนวณ ในสถานการณ์ที่ต้องใช้ในการคำนวณทางการเงิน ความผิดพลาดเหล่านี้อาจสะสมขึ้นตามเวลาและทำให้เกิดปัญหาที่สำคัญ
ค: ทำไมไม่ใช้ big.Rat โดยตรง? ก: แม้ว่า big.Rat สามารถแทนเลขส่วนร้อยได้ แต่มันไม่เหมาะสมสำหรับการแทนเลขเงิน ตัวเลขทศนิยมเหมาะสำหรับการคำนวณทางการเงินเนื่องจากมันสามารถแทนเลขทศนิยมได้อย่างถูกต้องโดยไม่สูญเสียความแม่นยำ
ค: ทำไม API ไม่คล้ายกับ big.Int? ก: API ของไลบรารี่ Decimal จะให้ความสำคัญกับการใช้งานและความถูกต้องมากกว่าความเร็ว ถึงแม้ว่า API ของ big.Int จะลดการจัดสร้างหน่วยความจำเพื่อเหตุผลที่เกี่ยวกับประสิทธิภาพ แต่มันอาจทำให้โค้ดที่ซับซ้อนและแรงดันผิดพลาด API ของไลบรารี่ Decimal ถูกออกแบบให้เป็นไปอย่างง่ายและเข้าใจได้ง่าย