1. Wprowadzenie

Biblioteka Decimal w języku Go jest potężnym narzędziem do obsługi liczb dziesiętnych o dowolnej precyzji w języku Go. Umożliwia dodawanie, odejmowanie, mnożenie i dzielenie operacji bez utraty precyzji. Dodatkowo zapewnia funkcje takie jak serializacja/deserializacja bazy danych/SQL, jak również serializacja/deserializacja JSON/XML.

2. Instalacja

Aby zainstalować bibliotekę Decimal w języku Go, można użyć poniższej komendy:

go get github.com/shopspring/decimal

Należy pamiętać, że biblioteka Decimal wymaga wersji Go >=1.7.

3. Podstawowe użycie

Aby użyć biblioteki Decimal w programie Go, zaimportuj pakiet "github.com/shopspring/decimal". Poniżej przedstawiono prosty przykładowy kod demonstrujący podstawowe użycie:

pakiet główny

import (
	"fmt"
	"github.com/shopspring/decimal"
)

funkcja główna() {
	cena, błąd := decimal.NewFromString("136.02")
	jeśli błąd != nil {
		panic(błąd)
	}

	ilość := decimal.NewFromInt(3)

	opłata, _ := decimal.NewFromString(".035")
	stawkaPodatku, _ := decimal.NewFromString(".08875")

	sumaCzęściowa := cena.Mul(ilość)
	przedPodatkiem := sumaCzęściowa.Mul(opłata).Add(decimal.NewFromFloat(1))
	całkowita := przedPodatkiem.Mul(stawkaPodatku).Add(decimal.NewFromFloat(1))

	fmt.Println("Suma częściowa:", sumaCzęściowa)                  // Suma częściowa: 408.06
	fmt.Println("Przed opodatkowaniem:", przedPodatkiem)          // Przed opodatkowaniem: 422.3421
	fmt.Println("Podatki:", całkowita.Sub(przedPodatkiem))         // Podatki: 37.482861375
	fmt.Println("Całkowita:", całkowita)                           // Całkowita: 459.824961375
	fmt.Println("Stawka podatkowa:", całkowita.Sub(przedPodatkiem).Div(przedPodatkiem)) // Stawka podatkowa: 0.08875
}

4. Tworzenie zmiennych Decimal

Biblioteka Decimal udostępnia różne metody tworzenia zmiennych Decimal. Oto obsługiwane interfejsy 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. Operacje arytmetyczne

Biblioteka Decimal w języku Go udostępnia kilka operacji arytmetycznych, które można wykonać na zmiennych Decimal. Oto obsługiwane operacje:

  • Add(d2 Decimal) Decimal: Dodaje dwie wartości Decimal i zwraca wynik.
  • Sub(d2 Decimal) Decimal: Odejmuje jedną wartość Decimal od drugiej i zwraca wynik.
  • Div(d2 Decimal) Decimal: Dzieli jedną wartość Decimal przez inną i zwraca wynik.
  • DivRound(d2 Decimal, precision int32) Decimal: Dzieli jedną wartość Decimal przez inną i zwraca wynik z określoną precyzją.
  • Mod(d2 Decimal) Decimal: Oblicza resztę z dzielenia jednej wartości Decimal przez inną i zwraca wynik.
  • Mul(d2 Decimal) Decimal: Mnoży dwie wartości Decimal i zwraca wynik.

Można użyć tych operacji do wykonywania standardowych obliczeń arytmetycznych na wartościach Decimal. Poniżej przedstawiono przykład wykorzystania tych operacji:

cena, _ := decimal.NewFromString("136.02")
ilość := decimal.NewFromInt(3)

sumaCzęściowa := cena.Mul(ilość)
podatek := sumaCzęściowa.Mul(decimal.NewFromFloat(0.08875))

całkowita := sumaCzęściowa.Add(podatek)

fmt.Println("Suma częściowa:", sumaCzęściowa) // Suma częściowa: 408.06
fmt.Println("Podatek:", podatek)             // Podatek: 36.244985
fmt.Println("Całkowita:", całkowita)         // Całkowita: 444.304985

W powyższym przykładzie używamy metody Mul() do obliczenia sumaCzęściowa poprzez pomnożenie cena i ilość. Następnie obliczamy podatek poprzez pomnożenie sumaCzęściowa przez stawkę podatku. Na końcu obliczamy całkowita poprzez dodanie sumaCzęściowa i podatek za pomocą metody Add().

6. Operacje zaokrąglania

Biblioteka Go Decimal zapewnia kilka operacji zaokrąglania, które można użyć do zaokrąglania wartości Decimal do określonej precyzji. Oto kilka dostępnych operacji zaokrąglania:

  • Round(places int32) Decimal: Zaokrągla wartość dziesiętną do określonej liczby miejsc po przecinku.
  • RoundBank(places int32) Decimal: Zaokrągla wartość dziesiętną zgodnie z zaokrąglaniem bankowym do określonej liczby miejsc po przecinku.
  • RoundCash(interval uint8) Decimal: Zaokrągla wartość dziesiętną do określonego interwału, takiego jak 5 centów, 10 centów, 25 centów, 50 centów lub 1 dolara.
  • RoundCeil(places int32) Decimal: Zaokrągla wartość dziesiętną w kierunku dodatniej nieskończoności.
  • RoundDown(places int32) Decimal: Zaokrągla wartość dziesiętną w kierunku zera.
  • RoundFloor(places int32) Decimal: Zaokrągla wartość dziesiętną w kierunku ujemnej nieskończoności.
  • RoundUp(places int32) Decimal: Zaokrągla wartość dziesiętną w kierunku od zera.

6.1. Round

Round zaokrągla wartość dziesiętną do określonej liczby miejsc po przecinku. Jeśli places < 0, zaokrągla część całkowitą do najbliższej 10^(-places).

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

6.2. RoundBank

RoundBank zaokrągla wartość dziesiętną do miejsc po przecinku. Jeśli odległość między ostatnią cyfrą do zaokrąglenia a najbliższymi dwoma liczbami całkowitymi jest równa, zaokrąglona wartość przyjmuje parzystą liczbę.

Jeśli places < 0, część całkowita zostanie zaokrąglona do najbliższej 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 (znane również jako zaokrąglanie gotówkowe/monet/polskie) zaokrągla wartość dziesiętną do konkretnych interwałów. Kwota płatna w transakcji gotówkowej zostanie zaokrąglona do najbliższej wielokrotności najmniejszej jednostki waluty. Dostępne interwały to: 5, 10, 25, 50 i 100; jakakolwiek inna liczba spowoduje wyjątek.

 5:   Zaokrąglanie do 5 centów 3.43 => 3.45
10:  Zaokrąglanie do 10 centów 3.45 => 3.50 (5 jest zaokrąglane w górę)
25:  Zaokrąglanie do 25 centów 3.41 => 3.50
50:  Zaokrąglanie do 50 centów 3.75 => 4.00
100: Zaokrąglanie do 100 centów 3.50 => 4.00

6.4. RoundCeil

RoundCeil zaokrągla wartość dziesiętną w kierunku dodatniej nieskończoności.

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 zaokrągla wartość dziesiętną w kierunku zera.

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 zaokrągla liczbę zmiennoprzecinkową w kierunku ujemnej nieskończoności.

NewFromFloat(545).RoundFloor(-2).String()   // Wynik: "500"
NewFromFloat(-500).RoundFloor(-2).String()   // Wynik: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // Wynik: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // Wynik: "-1.4"

6.7. RoundUp

RoundUp zaokrągla liczbę zmiennoprzecinkową w kierunku od zera.

NewFromFloat(545).RoundUp(-2).String()   // Wynik: "600"
NewFromFloat(500).RoundUp(-2).String()   // Wynik: "500"
NewFromFloat(1.1001).RoundUp(2).String() // Wynik: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // Wynik: "-1.4"

7. Konwertowanie typu Decimal do stringa

Biblioteka Go Decimal udostępnia metody do konwertowania wartości Decimal na reprezentacje stringa. Oto kilka dostępnych metod:

  • String(): string: Zwraca reprezentację stringową liczby dziesiętnej z ustalonym miejscem dziesiętnym.
  • StringFixed(places int32) string: Zwraca zaokrągloną reprezentację stringową z określoną liczbą miejsc dziesiętnych.
  • StringFixedBank(places int32) string: Zwraca zaokrągloną (zaokrąglanie do najbliższej parzystej) reprezentację stringową z określoną liczbą miejsc dziesiętnych.

Możesz wybrać odpowiednią metodę zgodnie z Twoimi potrzebami. Poniżej znajduje się przykład konwertowania liczby dziesiętnej na stringa:

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

fmt.Println("Reprezentacja stringowa liczby dziesiętnej:", str) // Reprezentacja stringowa liczby dziesiętnej: 5.45

// Przykład użycia StringFixed
NewFromFloat(0).StringFixed(2) // Wynik: "0.00"
NewFromFloat(0).StringFixed(0) // Wynik: "0"
NewFromFloat(5.45).StringFixed(0) // Wynik: "5"
NewFromFloat(5.45).StringFixed(1) // Wynik: "5.5"
NewFromFloat(5.45).StringFixed(2) // Wynik: "5.45"
NewFromFloat(5.45).StringFixed(3) // Wynik: "5.450"
NewFromFloat(545).StringFixed(-1) // Wynik: "550"

// Przykład użycia StringFixedBank
NewFromFloat(0).StringFixedBank(2) // Wynik: "0.00"
NewFromFloat(0).StringFixedBank(0) // Wynik: "0"
NewFromFloat(5.45).StringFixedBank(0) // Wynik: "5"
NewFromFloat(5.45).StringFixedBank(1) // Wynik: "5.4"
NewFromFloat(5.45).StringFixedBank(2) // Wynik: "5.45"
NewFromFloat(5.45).StringFixedBank(3) // Wynik: "5.450"
NewFromFloat(545).StringFixedBank(-1) // Wynik: "540"

8. Często zadawane pytania

Q: Dlaczego nie używać bezpośrednio float64? A: float64 nie może dokładnie reprezentować liczb takich jak 0.1, co może prowadzić do małych błędów. W sytuacjach związanych z obliczeniami finansowymi te błędy mogą gromadzić się w czasie i powodować poważne problemy.

Q: Dlaczego nie używać bezpośrednio big.Rat? A: Chociaż big.Rat może reprezentować liczby wymierne, nie nadaje się do reprezentowania waluty. Liczby dziesiętne są lepsze do obliczeń finansowych, ponieważ mogą dokładnie reprezentować ułamki dziesiętne bez utraty precyzji.

Q: Dlaczego API nie jest podobne do big.Int? A: API biblioteki Decimal stawia na użyteczność i poprawność kosztem wydajności. Podczas gdy API big.Int redukuje alokacje pamięci ze względu na wydajność, może to prowadzić do złożonego i podatnego na błędy kodu. API biblioteki Decimal zostało zaprojektowane tak, aby było proste i łatwe do zrozumienia.