1. Introdução
A biblioteca Decimal Go é uma ferramenta poderosa para lidar com decimais de ponto fixo de precisão arbitrária na linguagem Go. Ela permite realizar operações de adição, subtração, multiplicação e divisão sem perder precisão. Além disso, fornece funcionalidades como serialização/desserialização para bancos de dados/SQL, bem como serialização/desserialização para JSON/XML.
2. Instalação
Para instalar a biblioteca Decimal Go, você pode utilizar o seguinte comando:
go get github.com/shopspring/decimal
Por favor, note que a biblioteca Decimal requer Go versão >=1.7.
3. Uso Básico
Para usar a biblioteca Decimal em um programa Go, importe o pacote "github.com/shopspring/decimal". Aqui está um exemplo simples demonstrando o uso básico:
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("Pré-imposto:", preTax) // Pré-imposto: 422.3421
fmt.Println("Impostos:", total.Sub(preTax)) // Impostos: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Taxa de imposto:", total.Sub(preTax).Div(preTax)) // Taxa de imposto: 0.08875
}
4. Criação de Variáveis Decimais
A biblioteca Decimal fornece vários métodos para criar variáveis Decimais. Abaixo estão as APIs suportadas:
-
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. Operações Aritméticas
A biblioteca Decimal Go fornece diversas operações aritméticas que podem ser realizadas em variáveis Decimais. Aqui estão algumas operações suportadas:
-
Add(d2 Decimal) Decimal
: Adiciona dois valores Decimais e retorna o resultado. -
Sub(d2 Decimal) Decimal
: Subtrai um valor Decimal de outro e retorna o resultado. -
Div(d2 Decimal) Decimal
: Divide um valor Decimal por outro e retorna o resultado. -
DivRound(d2 Decimal, precision int32) Decimal
: Divide um valor Decimal por outro e retorna o resultado com a precisão especificada. -
Mod(d2 Decimal) Decimal
: Calcula o módulo (restante) de um valor Decimal dividido por outro e retorna o resultado. -
Mul(d2 Decimal) Decimal
: Multiplica dois valores Decimais e retorna o resultado.
Você pode utilizar essas operações para realizar cálculos aritméticos comuns em valores Decimais. Aqui está um exemplo demonstrando o uso dessas operações:
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("Imposto:", tax) // Imposto: 36.244985
fmt.Println("Total:", total) // Total: 444.304985
No exemplo acima, utilizamos o método Mul()
para calcular o subtotal
multiplicando price
e quantity
. Em seguida, calculamos o imposto
multiplicando o subtotal
pela taxa de imposto. Por fim, calculamos o total
adicionando o subtotal
e o imposto
utilizando o método Add()
.
6. Operações de Arredondamento
A biblioteca Decimal do Go fornece diversas operações de arredondamento que podem ser usadas para arredondar valores decimais para uma precisão específica. Aqui estão algumas operações de arredondamento disponíveis:
-
Round(places int32) Decimal
: Arredonda o decimal para o número especificado de casas decimais. -
RoundBank(places int32) Decimal
: Arredonda o decimal usando o arredondamento do banqueiro para o número especificado de casas decimais. -
RoundCash(interval uint8) Decimal
: Arredonda o decimal para um intervalo específico, como 5 centavos, 10 centavos, 25 centavos, 50 centavos ou 1 real. -
RoundCeil(places int32) Decimal
: Arredonda o decimal em direção ao infinito positivo. -
RoundDown(places int32) Decimal
: Arredonda o decimal em direção a zero. -
RoundFloor(places int32) Decimal
: Arredonda o decimal em direção ao infinito negativo. -
RoundUp(places int32) Decimal
: Arredonda o decimal para longe de zero.
6.1. Arredondar
A função Round
arredonda o decimal para o número especificado de casas decimais. Se places < 0, arredonda a parte inteira para o múltiplo mais próximo de 10^(-places).
NewFromFloat(5.45).Round(1).String() // Saída: "5.5"
NewFromFloat(545).Round(-1).String() // Saída: "550"
6.2. Arredondar com Banqueiro
A função RoundBank
arredonda o decimal para places casas decimais. Se a distância entre o último dígito a ser arredondado e os dois inteiros mais próximos for igual, o valor arredondado é o número par.
Se places < 0, a parte inteira será arredondada para o múltiplo mais próximo de 10^(-places).
NewFromFloat(5.45).RoundBank(1).String() // Saída: "5.4"
NewFromFloat(545).RoundBank(-1).String() // Saída: "540"
NewFromFloat(5.46).RoundBank(1).String() // Saída: "5.5"
NewFromFloat(546).RoundBank(-1).String() // Saída: "550"
NewFromFloat(5.55).RoundBank(1).String() // Saída: "5.6"
NewFromFloat(555).RoundBank(-1).String() // Saída: "560"
6.3. Arredondar para Cash
A função RoundCash
(também conhecida como arredondamento de centavos) arredonda o decimal para intervalos específicos. O valor pagável de uma transação em dinheiro será arredondado para o múltiplo mais próximo da unidade monetária menor. Os intervalos disponíveis são: 5, 10, 25, 50 e 100; qualquer outro número resultará em uma exceção.
5: Arredondamento de 5 centavos 3.43 => 3.45
10: Arredondamento de 10 centavos 3.45 => 3.50 (5 é arredondado para cima)
25: Arredondamento de 25 centavos 3.41 => 3.50
50: Arredondamento de 50 centavos 3.75 => 4.00
100: Arredondamento de 100 centavos 3.50 => 4.00
6.4. Arredondar para Cima
A função RoundCeil
arredonda o decimal em direção ao infinito positivo.
NewFromFloat(545).RoundCeil(-2).String() // Saída: "600"
NewFromFloat(500).RoundCeil(-2).String() // Saída: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // Saída: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // Saída: "-1.5"
6.5. Arredondar para Baixo
A função RoundDown
arredonda o decimal em direção a zero.
NewFromFloat(545).RoundDown(-2).String() // Saída: "500"
NewFromFloat(-500).RoundDown(-2).String() // Saída: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // Saída: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // Saída: "-1.5"
6.6. Arredondar para Baixo (RoundFloor)
O RoundFloor arredonda o decimal em direção ao infinito negativo.
NewFromFloat(545).RoundFloor(-2).String() // Saída: "500"
NewFromFloat(-500).RoundFloor(-2).String() // Saída: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // Saída: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // Saída: "-1.4"
6.7. Arredondar para Cima (RoundUp)
O RoundUp arredonda o decimal para longe de zero.
NewFromFloat(545).RoundUp(-2).String() // Saída: "600"
NewFromFloat(500).RoundUp(-2).String() // Saída: "500"
NewFromFloat(1.1001).RoundUp(2).String() // Saída: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // Saída: "-1.4"
7. Converter Tipo Decimal para String
A biblioteca Decimal do Go fornece métodos para converter valores decimais em representações de string. Aqui estão alguns métodos disponíveis:
-
String(): string
: Retorna a representação de string do número decimal com um ponto decimal fixo. -
StringFixed(places int32) string
: Retorna a representação de string arredondada com um número especificado de casas decimais. -
StringFixedBank(places int32) string
: Retorna a representação de string arredondada (arredondamento bancário) com um número especificado de casas decimais.
Você pode escolher o método apropriado de acordo com suas necessidades. Abaixo está um exemplo de conversão de um número decimal para uma string:
d := decimal.NewFromFloat(5.45)
str := d.String()
fmt.Println("Representação de string do número decimal:", str) // Representação de string do número decimal: 5.45
// Exemplo de StringFixed
NewFromFloat(0).StringFixed(2) // Saída: "0.00"
NewFromFloat(0).StringFixed(0) // Saída: "0"
NewFromFloat(5.45).StringFixed(0) // Saída: "5"
NewFromFloat(5.45).StringFixed(1) // Saída: "5.5"
NewFromFloat(5.45).StringFixed(2) // Saída: "5.45"
NewFromFloat(5.45).StringFixed(3) // Saída: "5.450"
NewFromFloat(545).StringFixed(-1) // Saída: "550"
// Exemplo de StringFixedBank
NewFromFloat(0).StringFixedBank(2) // Saída: "0.00"
NewFromFloat(0).StringFixedBank(0) // Saída: "0"
NewFromFloat(5.45).StringFixedBank(0) // Saída: "5"
NewFromFloat(5.45).StringFixedBank(1) // Saída: "5.4"
NewFromFloat(5.45).StringFixedBank(2) // Saída: "5.45"
NewFromFloat(5.45).StringFixedBank(3) // Saída: "5.450"
NewFromFloat(545).StringFixedBank(-1) // Saída: "540"
8. Perguntas Comuns
P: Por que não usar float64 diretamente? R: float64 não pode representar com precisão números como 0.1, o que pode levar a pequenos erros. Em situações envolvendo cálculos financeiros, esses erros podem se acumular ao longo do tempo e causar problemas significativos.
Q: Por que não usar big.Rat diretamente? A: Embora big.Rat possa representar números racionais, não é adequado para representar moeda. Números decimais são melhores para cálculos financeiros, pois podem representar com precisão frações decimais sem perder a precisão.
Q: Por que a API não é semelhante à big.Int? A: A API da biblioteca Decimal prioriza a usabilidade e a correção em relação ao desempenho. Enquanto a API de big.Int reduz alocações de memória por razões de desempenho, isso pode resultar em código complexo e propenso a erros. A API da biblioteca Decimal foi projetada para ser simples e fácil de entender.