1. Introducción
La biblioteca Go Decimal es una herramienta poderosa para manejar decimales de punto fijo de precisión arbitraria en el lenguaje Go. Permite realizar operaciones de suma, resta, multiplicación y división sin perder precisión. Además, proporciona funcionalidades como serialización/deserialización de base de datos/SQL, así como serialización/deserialización de JSON/XML.
2. Instalación
Para instalar la biblioteca Go Decimal, puedes usar el siguiente comando:
go get github.com/shopspring/decimal
Ten en cuenta que la biblioteca Decimal requiere Go versión >=1.7.
3. Uso básico
Para utilizar la biblioteca Decimal en un programa Go, importa el paquete "github.com/shopspring/decimal". Aquí tienes un código de ejemplo sencillo que demuestra el uso básico:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
precio, err := decimal.NewFromString("136.02")
if err != nil {
panic(err)
}
cantidad := decimal.NewFromInt(3)
comisión, _ := decimal.NewFromString(".035")
tasaImpuesto, _ := decimal.NewFromString(".08875")
subtotal := precio.Mul(cantidad)
antesImpuesto := subtotal.Mul(comisión).Add(decimal.NewFromFloat(1))
total := antesImpuesto.Mul(tasaImpuesto).Add(decimal.NewFromFloat(1))
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
fmt.Println("Antes de impuestos:", antesImpuesto) // Antes de impuestos: 422.3421
fmt.Println("Impuestos:", total.Sub(antesImpuesto)) // Impuestos: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Tasa de impuesto:", total.Sub(antesImpuesto).Div(antesImpuesto)) // Tasa de impuesto: 0.08875
}
4. Creación de variables Decimal
La biblioteca Decimal proporciona varios métodos para crear variables Decimal. A continuación se muestran las API admitidas:
-
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. Operaciones aritméticas
La biblioteca Go Decimal proporciona varias operaciones aritméticas que se pueden realizar en variables Decimal. Aquí se muestran algunas operaciones admitidas:
-
Add(d2 Decimal) Decimal
: Suma dos valores Decimal y devuelve el resultado. -
Sub(d2 Decimal) Decimal
: Resta un valor Decimal de otro y devuelve el resultado. -
Div(d2 Decimal) Decimal
: Divide un valor Decimal entre otro y devuelve el resultado. -
DivRound(d2 Decimal, precision int32) Decimal
: Divide un valor Decimal entre otro y devuelve el resultado con la precisión especificada. -
Mod(d2 Decimal) Decimal
: Calcula el módulo (resto) de un valor Decimal dividido entre otro y devuelve el resultado. -
Mul(d2 Decimal) Decimal
: Multiplica dos valores Decimal y devuelve el resultado.
Puedes utilizar estas operaciones para realizar cálculos aritméticos comunes en valores Decimal. Aquí tienes un ejemplo que demuestra el uso de estas operaciones:
precio, _ := decimal.NewFromString("136.02")
cantidad := decimal.NewFromInt(3)
subtotal := precio.Mul(cantidad)
impuesto := subtotal.Mul(decimal.NewFromFloat(0.08875))
total := subtotal.Add(impuesto)
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
fmt.Println("Impuesto:", impuesto) // Impuesto: 36.244985
fmt.Println("Total:", total) // Total: 444.304985
En el ejemplo anterior, utilizamos el método Mul()
para calcular subtotal
multiplicando precio
y cantidad
. Luego calculamos impuesto
multiplicando subtotal
por la tasa de impuesto. Finalmente, calculamos total
sumando subtotal
e impuesto
utilizando el método Add()
.
6. Operaciones de Redondeo
La biblioteca Decimal de Go proporciona varias operaciones de redondeo que se pueden usar para redondear valores decimales a una precisión específica. A continuación se muestran algunas operaciones de redondeo disponibles:
-
Round(places int32) Decimal
: Redondea el decimal al número especificado de lugares decimales. -
RoundBank(places int32) Decimal
: Redondea el decimal utilizando el redondeo bancario al número especificado de lugares decimales. -
RoundCash(interval uint8) Decimal
: Redondea el decimal a un intervalo específico, como 5 centavos, 10 centavos, 25 centavos, 50 centavos o 1 dólar. -
RoundCeil(places int32) Decimal
: Redondea el decimal hacia el infinito positivo. -
RoundDown(places int32) Decimal
: Redondea el decimal hacia cero. -
RoundFloor(places int32) Decimal
: Redondea el decimal hacia el infinito negativo. -
RoundUp(places int32) Decimal
: Redondea el decimal lejos de cero.
6.1. Round
Round redondea el decimal al número especificado de lugares decimales. Si places < 0, redondea la parte entera al número más cercano de 10^(-places).
NewFromFloat(5.45).Round(1).String() // Salida: "5.5"
NewFromFloat(545).Round(-1).String() // Salida: "550"
6.2. RoundBank
RoundBank redondea el decimal a places lugares decimales. Si la distancia entre el último dígito a redondear y los dos enteros más cercanos es igual, el valor redondeado toma el número par.
Si places < 0, la parte entera se redondeará al número más cercano de 10^(-places).
NewFromFloat(5.45).RoundBank(1).String() // Salida: "5.4"
NewFromFloat(545).RoundBank(-1).String() // Salida: "540"
NewFromFloat(5.46).RoundBank(1).String() // Salida: "5.5"
NewFromFloat(546).RoundBank(-1).String() // Salida: "550"
NewFromFloat(5.55).RoundBank(1).String() // Salida: "5.6"
NewFromFloat(555).RoundBank(-1).String() // Salida: "560"
6.3. RoundCash
RoundCash (también conocido como redondeo de efectivo/centavo/irlandés) redondea el decimal a intervalos específicos. El monto pagadero de una transacción en efectivo se redondeará al múltiplo más cercano de la unidad de moneda más pequeña. Los intervalos disponibles son: 5, 10, 25, 50 y 100; cualquier otro número resultará en una excepción.
5: Redondeo a 5 centavos 3.43 => 3.45
10: Redondeo a 10 centavos 3.45 => 3.50 (5 se redondea hacia arriba)
25: Redondeo a 25 centavos 3.41 => 3.50
50: Redondeo a 50 centavos 3.75 => 4.00
100: Redondeo a 100 centavos 3.50 => 4.00
6.4. RoundCeil
RoundCeil redondea el decimal hacia el infinito positivo.
NewFromFloat(545).RoundCeil(-2).String() // Salida: "600"
NewFromFloat(500).RoundCeil(-2).String() // Salida: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // Salida: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // Salida: "-1.5"
6.5. RoundDown
RoundDown redondea el decimal hacia cero.
NewFromFloat(545).RoundDown(-2).String() // Salida: "500"
NewFromFloat(-500).RoundDown(-2).String() // Salida: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // Salida: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // Salida: "-1.5"
6.6. RoundFloor
RoundFloor redondea el decimal hacia menos infinito.
NewFromFloat(545).RoundFloor(-2).String() // Salida: "500"
NewFromFloat(-500).RoundFloor(-2).String() // Salida: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // Salida: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // Salida: "-1.4"
6.7. RoundUp
RoundUp redondea el decimal lejos de cero.
NewFromFloat(545).RoundUp(-2).String() // Salida: "600"
NewFromFloat(500).RoundUp(-2).String() // Salida: "500"
NewFromFloat(1.1001).RoundUp(2).String() // Salida: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // Salida: "-1.4"
7. Convertir tipo Decimal a String
La biblioteca Decimal de Go proporciona métodos para convertir valores decimales a representaciones de cadena. Aquí hay algunos métodos disponibles:
-
String(): string
: Devuelve la representación de cadena del número decimal con un punto decimal fijo. -
StringFixed(places int32) string
: Devuelve la representación de cadena redondeada con un número especificado de lugares decimales. -
StringFixedBank(places int32) string
: Devuelve la representación de cadena redondeada (redondeo bancario) con un número especificado de lugares decimales.
Puede elegir el método apropiado según sus necesidades. A continuación se muestra un ejemplo de conversión de un número decimal a una cadena:
d := decimal.NewFromFloat(5.45)
str := d.String()
fmt.Println("Representación de cadena del número decimal:", str) // Representación de cadena del número decimal: 5.45
// Ejemplo de StringFixed
NewFromFloat(0).StringFixed(2) // Salida: "0.00"
NewFromFloat(0).StringFixed(0) // Salida: "0"
NewFromFloat(5.45).StringFixed(0) // Salida: "5"
NewFromFloat(5.45).StringFixed(1) // Salida: "5.5"
NewFromFloat(5.45).StringFixed(2) // Salida: "5.45"
NewFromFloat(5.45).StringFixed(3) // Salida: "5.450"
NewFromFloat(545).StringFixed(-1) // Salida: "550"
// Ejemplo de StringFixedBank
NewFromFloat(0).StringFixedBank(2) // Salida: "0.00"
NewFromFloat(0).StringFixedBank(0) // Salida: "0"
NewFromFloat(5.45).StringFixedBank(0) // Salida: "5"
NewFromFloat(5.45).StringFixedBank(1) // Salida: "5.4"
NewFromFloat(5.45).StringFixedBank(2) // Salida: "5.45"
NewFromFloat(5.45).StringFixedBank(3) // Salida: "5.450"
NewFromFloat(545).StringFixedBank(-1) // Salida: "540"
8. Preguntas comunes
P: ¿Por qué no usar float64 directamente? R: float64 no puede representar con precisión números como 0.1, lo que puede provocar pequeños errores. En situaciones que involucran cálculos financieros, estos errores pueden acumularse con el tiempo y causar problemas significativos.
P: ¿Por qué no usar big.Rat directamente? R: Aunque big.Rat puede representar números racionales, no es adecuado para representar moneda. Los números decimales son mejores para cálculos financieros, ya que pueden representar con precisión fracciones decimales sin perder precisión.
P: ¿Por qué la API no es similar a big.Int? R: La API de la biblioteca Decimal prioriza la usabilidad y la corrección sobre el rendimiento. Mientras que la API de big.Int reduce las asignaciones de memoria por razones de rendimiento, puede resultar en código complejo y propenso a errores. La API de la biblioteca Decimal está diseñada para ser simple y fácil de entender.