1. Introduzione

La libreria Go Decimal è uno strumento potente per gestire decimali a virgola fissa a precisione arbitraria nel linguaggio Go. Consente di eseguire operazioni di addizione, sottrazione, moltiplicazione e divisione senza perdere precisione. Inoltre, fornisce funzionalità come la serializzazione/deserializzazione del database/SQL, nonché la serializzazione/deserializzazione JSON/XML.

2. Installazione

Per installare la libreria Go Decimal, è possibile utilizzare il seguente comando:

go get github.com/shopspring/decimal

Si prega di notare che la libreria Decimal richiede Go versione >=1.7.

3. Utilizzo di base

Per utilizzare la libreria Decimal in un programma Go, importa il pacchetto "github.com/shopspring/decimal". Ecco un semplice esempio di codice che dimostra l'utilizzo di base:

package main

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

func main() {
	prezzo, err := decimal.NewFromString("136.02")
	if err != nil {
		panic(err)
	}

	quantità := decimal.NewFromInt(3)

	commissione, _ := decimal.NewFromString(".035")
	tariffaTasse, _ := decimal.NewFromString(".08875")

	subtotale := prezzo.Mul(quantità)
	preTasse := subtotale.Mul(commissione).Add(decimal.NewFromFloat(1))
	totale := preTasse.Mul(tariffaTasse).Add(decimal.NewFromFloat(1))

	fmt.Println("Subtotale:", subtotale)                                         // Subtotale: 408.06
	fmt.Println("Pre-tasse:", preTasse)                                           // Pre-tasse: 422.3421
	fmt.Println("Tasse:", totale.Sub(preTasse))                                   // Tasse: 37.482861375
	fmt.Println("Totale:", totale)                                                // Totale: 459.824961375
	fmt.Println("Tariffa tasse:", totale.Sub(preTasse).Div(preTasse))             // Tariffa tasse: 0.08875
}

4. Creazione di variabili Decimal

La libreria Decimal fornisce vari metodi per creare variabili Decimal. Di seguito sono riportate le API supportate:

  • 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. Operazioni aritmetiche

La libreria Go Decimal fornisce diverse operazioni aritmetiche che possono essere eseguite su variabili Decimal. Ecco alcune operazioni supportate:

  • Add(d2 Decimal) Decimal: Aggiunge due valori Decimal restituendo il risultato.
  • Sub(d2 Decimal) Decimal: Sottrae un valore Decimal da un altro e restituisce il risultato.
  • Div(d2 Decimal) Decimal: Divide un valore Decimal per un altro e restituisce il risultato.
  • DivRound(d2 Decimal, precision int32) Decimal: Divide un valore Decimal per un altro e restituisce il risultato con la precisione specificata.
  • Mod(d2 Decimal) Decimal: Calcola il modulo (resto) di un valore Decimal diviso per un altro e restituisce il risultato.
  • Mul(d2 Decimal) Decimal: Moltiplica due valori Decimal e restituisce il risultato.

È possibile utilizzare queste operazioni per eseguire calcoli aritmetici comuni su valori Decimal. Ecco un esempio che dimostra l'utilizzo di queste operazioni:

prezzo, _ := decimal.NewFromString("136.02")
quantità := decimal.NewFromInt(3)

subtotale := prezzo.Mul(quantità)
tasse := subtotale.Mul(decimal.NewFromFloat(0.08875))

totale := subtotale.Add(tasse)

fmt.Println("Subtotale:", subtotale) // Subtotale: 408.06
fmt.Println("Tasse:", tasse)         // Tasse: 36.244985
fmt.Println("Totale:", totale)       // Totale: 444.304985

Nell'esempio precedente, utilizziamo il metodo Mul() per calcolare il subtotale moltiplicando prezzo e quantità. Quindi calcoliamo le tasse moltiplicando subtotale per il tasso fiscale. Infine, calcoliamo il totale aggiungendo subtotale e tasse utilizzando il metodo Add().

6. Operazioni di Arrotondamento

La libreria Decimal di Go fornisce diverse operazioni di arrotondamento che possono essere utilizzate per arrotondare i valori Decimal a una precisione specifica. Ecco alcune operazioni di arrotondamento disponibili:

  • Round(places int32) Decimal: Arrotonda il decimale al numero specificato di cifre decimali.
  • RoundBank(places int32) Decimal: Arrotonda il decimale utilizzando l'arrotondamento bancario al numero specificato di cifre decimali.
  • RoundCash(interval uint8) Decimal: Arrotonda il decimale a un intervallo specifico, come 5 centesimi, 10 centesimi, 25 centesimi, 50 centesimi o 1 dollaro.
  • RoundCeil(places int32) Decimal: Arrotonda il decimale verso l'infinito positivo.
  • RoundDown(places int32) Decimal: Arrotonda il decimale verso lo zero.
  • RoundFloor(places int32) Decimal: Arrotonda il decimale verso l'infinito negativo.
  • RoundUp(places int32) Decimal: Arrotonda il decimale lontano dallo zero.

6.1. Round

Round arrotonda il decimale al numero specificato di cifre decimali. Se places < 0, arrotonda la parte intera al 10^(-places) più vicino.

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

6.2. RoundBank

RoundBank arrotonda il decimale a places cifre decimali. Se la distanza tra l'ultima cifra da arrotondare e i due interi più vicini è uguale, il valore arrotondato prende il numero pari.

Se places < 0, la parte intera verrà arrotondata al 10^(-places) più vicino.

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 (noto anche come arrotondamento cash/penny/irlandese) arrotonda il decimale a intervalli specifici. L'importo pagabile di una transazione in contanti verrà arrotondato al multiplo più vicino dell'unità monetaria più piccola. Gli intervalli disponibili sono: 5, 10, 25, 50 e 100; qualsiasi altro numero causerà un'eccezione.

  5:   arrotondamento a 5 centesimi 3.43 => 3.45
 10:  arrotondamento a 10 centesimi 3.45 => 3.50 (5 viene arrotondato per eccesso)
 25:  arrotondamento a 25 centesimi 3.41 => 3.50
 50:  arrotondamento a 50 centesimi 3.75 => 4.00
100: arrotondamento a 100 centesimi 3.50 => 4.00

6.4. RoundCeil

RoundCeil arrotonda il decimale verso l'infinito positivo.

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 arrotonda il decimale verso lo zero.

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 arrotonda il decimale verso meno infinito.

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 arrotonda il decimale lontano da zero.

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. Convertire il Tipo Decimal in Stringa

La libreria Decimal di Go fornisce metodi per convertire i valori Decimal in rappresentazioni stringa. Ecco alcuni metodi disponibili:

  • String(): string: Restituisce la rappresentazione stringa del numero decimale con un punto decimale fisso.
  • StringFixed(places int32) string: Restituisce la rappresentazione stringa arrotondata con un numero specificato di posizioni decimali.
  • StringFixedBank(places int32) string: Restituisce la rappresentazione stringa arrotondata (arrotondamento bancario) con un numero specificato di posizioni decimali.

Puoi scegliere il metodo appropriato in base alle tue esigenze. Di seguito è riportato un esempio di conversione di un numero decimale in una stringa:

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

fmt.Println("Rappresentazione stringa del numero decimale:", str) // Rappresentazione stringa del numero decimale: 5.45

// Esempio di 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"

// Esempio di 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. Domande Comuni

Q: Perché non usare direttamente float64? A: float64 non può rappresentare con precisione numeri come 0.1, il che può causare piccoli errori. In situazioni che coinvolgono calcoli finanziari, questi errori possono accumularsi nel tempo e causare problemi significativi.

Q: Perché non usare direttamente big.Rat? A: Anche se big.Rat può rappresentare numeri razionali, non è adatto per rappresentare valute. I numeri decimali sono migliori per calcoli finanziari in quanto possono rappresentare con precisione frazioni decimali senza perdere precisione.

Q: Perché l'API non è simile a big.Int? A: L'API della libreria Decimal dà priorità all'usabilità e alla correttezza rispetto alle prestazioni. Mentre l'API di big.Int riduce le assegnazioni di memoria per motivi di prestazioni, potrebbe portare a codice complesso e soggetto a errori. L'API della libreria Decimal è progettata per essere semplice e di facile comprensione.