1. Pengantar

Pustaka Decimal Go adalah alat yang powerful untuk menangani desimal titik tetap presisi sembarang dalam bahasa Go. Ini memungkinkan operasi penambahan, pengurangan, perkalian, dan pembagian tanpa kehilangan presisi. Selain itu, pustaka ini menyediakan fungsionalitas seperti serialisasi/deserialisasi database/SQL, serta serialisasi/deserialisasi JSON/XML.

2. Instalasi

Untuk menginstal pustaka Decimal Go, Anda dapat menggunakan perintah berikut:

go get github.com/shopspring/decimal

Harap dicatat bahwa pustaka Decimal memerlukan versi Go >=1.7.

3. Penggunaan Dasar

Untuk menggunakan pustaka Decimal dalam program Go, impor paket "github.com/shopspring/decimal". Berikut contoh kode sederhana yang menunjukkan penggunaan dasar:

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. Membuat Variabel Decimal

Pustaka Decimal menyediakan berbagai metode untuk membuat variabel Decimal. Berikut adalah API yang didukung:

  • 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. Operasi Aritmatika

Pustaka Decimal Go menyediakan beberapa operasi aritmatika yang dapat dilakukan pada variabel Decimal. Berikut beberapa operasi yang didukung:

  • Add(d2 Decimal) Decimal: Menambahkan dua nilai Decimal dan mengembalikan hasilnya.
  • Sub(d2 Decimal) Decimal: Mengurangkan satu nilai Decimal dari nilai lainnya dan mengembalikan hasilnya.
  • Div(d2 Decimal) Decimal: Membagi satu nilai Decimal dengan nilai lain dan mengembalikan hasilnya.
  • DivRound(d2 Decimal, precision int32) Decimal: Membagi satu nilai Decimal dengan nilai lain dan mengembalikan hasilnya dengan presisi yang ditentukan.
  • Mod(d2 Decimal) Decimal: Menghitung modulus (sisa bagi) dari satu nilai Decimal dibagi dengan nilai lain dan mengembalikan hasilnya.
  • Mul(d2 Decimal) Decimal: Mengalikan dua nilai Decimal dan mengembalikan hasilnya.

Anda dapat menggunakan operasi-operasi ini untuk melakukan perhitungan aritmatika umum pada nilai Decimal. Berikut contoh yang menunjukkan penggunaan operasi-operasi ini:

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

Pada contoh di atas, kami menggunakan metode Mul() untuk menghitung subtotal dengan mengalikan price dan quantity. Kemudian kami menghitung tax dengan mengalikan subtotal dengan tarif pajak. Akhirnya, kami menghitung total dengan menambahkan subtotal dan tax menggunakan metode Add().

6. Operasi Pembulatan

Library Go Decimal menyediakan beberapa operasi pembulatan yang dapat digunakan untuk membulatkan nilai Decimal ke presisi tertentu. Berikut adalah beberapa operasi pembulatan yang tersedia:

  • Round(places int32) Decimal: Membulatkan desimal ke jumlah tempat desimal yang ditentukan.
  • RoundBank(places int32) Decimal: Membulatkan desimal menggunakan pembulatan banker ke jumlah tempat desimal yang ditentukan.
  • RoundCash(interval uint8) Decimal: Membulatkan desimal ke interval tertentu, seperti 5 sen, 10 sen, 25 sen, 50 sen, atau 1 dolar.
  • RoundCeil(places int32) Decimal: Membulatkan desimal ke positif tak hingga.
  • RoundDown(places int32) Decimal: Membulatkan desimal ke nol.
  • RoundFloor(places int32) Decimal: Membulatkan desimal ke negatif tak hingga.
  • RoundUp(places int32) Decimal: Membulatkan desimal menjauh dari nol.

6.1. Round

Round membulatkan desimal ke jumlah tempat desimal yang ditentukan. Jika places < 0, maka akan membulatkan bagian bilangan bulat ke 10^(-places) terdekat.

NewFromFloat(5.45).Round(1).String() // Output: "5.5"
NewFromFloat(545).Round(-1).String() // Output: "550"

6.2. RoundBank

RoundBank membulatkan desimal ke jumlah tempat desimal yang ditentukan. Jika jarak antara digit terakhir yang akan dibulatkan dan dua bilangan bulat terdekat sama, nilai yang dibulatkan akan mengambil bilangan genap.

Jika places < 0, maka bagian bilangan bulat akan dibulatkan ke 10^(-places) terdekat.

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 (juga dikenal dengan nama pembulatan uang/penny/Ireland) membulatkan desimal ke interval tertentu. Jumlah pembayaran dari transaksi tunai akan dibulatkan ke kelipatan terdekat dari satuan mata uang terkecil. Interval yang tersedia adalah: 5, 10, 25, 50, dan 100; angka lain akan menyebabkan pengecualian.

   5:   pembulatan 5 sen 3.43 => 3.45
  10:  pembulatan 10 sen 3.45 => 3.50 (5 dibulatkan ke atas)
  25:  pembulatan 25 sen 3.41 => 3.50
  50:  pembulatan 50 sen 3.75 => 4.00
100: pembulatan 100 sen 3.50 => 4.00

6.4. RoundCeil

RoundCeil membulatkan desimal ke positif tak hingga.

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 membulatkan desimal ke nol.

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 membulatkan desimal ke nol negatif.

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 membulatkan desimal menjauhi nol.

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. Mengonversi Tipe Decimal ke String

Pustaka Go Decimal menyediakan metode untuk mengonversi nilai Decimal ke representasi string. Berikut beberapa metode yang tersedia:

  • String(): string: Mengembalikan representasi string dari angka desimal dengan titik desimal tetap.
  • StringFixed(places int32) string: Mengembalikan representasi string yang dibulatkan dengan jumlah tempat desimal yang ditentukan.
  • StringFixedBank(places int32) string: Mengembalikan representasi string yang dibulatkan (pembulatan bankir) dengan jumlah tempat desimal yang ditentukan.

Anda dapat memilih metode yang sesuai sesuai kebutuhan. Berikut contoh mengonversi angka desimal menjadi string:

d := decimal.NewFromFloat(5.45)
str := d.String()

fmt.Println("Representasi string dari angka desimal:", str) // Representasi string dari angka desimal: 5.45

// Contoh 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"

// Contoh 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. Pertanyaan Umum

Q: Mengapa tidak menggunakan float64 secara langsung? A: float64 tidak dapat mewakili angka seperti 0.1 dengan akurat, yang dapat menyebabkan kesalahan kecil. Dalam situasi yang melibatkan perhitungan keuangan, kesalahan-kesalahan ini dapat bertambah dari waktu ke waktu dan menimbulkan masalah signifikan.

Q: Mengapa tidak menggunakan big.Rat secara langsung? A: Meskipun big.Rat dapat mewakili angka rasional, itu tidak cocok untuk mewakili mata uang. Angka desimal lebih baik untuk perhitungan keuangan karena mereka dapat mewakili pecahan desimal secara akurat tanpa kehilangan presisi.

Q: Mengapa API-nya tidak mirip dengan big.Int? A: API dari pustaka Decimal memprioritaskan kegunaan dan kebenaran daripada kinerja. Sementara API dari big.Int mengurangi alokasi memori atas alasan kinerja, hal ini dapat menyebabkan kode yang kompleks dan rentan terhadap kesalahan. API dari pustaka Decimal dirancang agar sederhana dan mudah dipahami.