Arbitrary-precision fixed-point decimal numbers for Go, avoiding floating-point precision issues with support for arithmetic, rounding, serialization, and database integration.
Complete guide to arithmetic operations with practical examples.
| Operation | Method | Example |
|---|---|---|
| Addition | Add(d2) | a.Add(b) → a + b |
| Subtraction | Sub(d2) | a.Sub(b) → a - b |
| Multiplication | Mul(d2) | a.Mul(b) → a * b |
| Division | Div(d2) | a.Div(b) → a / b |
| Modulo | Mod(d2) | a.Mod(b) → a % b |
| Negation | Neg() | a.Neg() → -a |
| Absolute | Abs() | a.Abs() → |a| |
| Shift | Shift(n) | a.Shift(2) → a * 10² |
All operations return NEW Decimal values. Originals are unchanged.
a := decimal.NewFromInt(10)
b := a.Add(decimal.NewFromInt(5))
// a is still 10
// b is 15// Calculate: (100 + 50) * 1.08 - 10
result := decimal.NewFromInt(100).
Add(decimal.NewFromInt(50)). // 150
Mul(decimal.NewFromFloat(1.08)). // 162
Sub(decimal.NewFromInt(10)) // 152func (d Decimal) Add(d2 Decimal) Decimal
// Returns d + d2Examples:
a := decimal.NewFromInt(100)
b := decimal.NewFromFloat(23.50)
a.Add(b) // 123.50
decimal.NewFromString("19.99").Add(
decimal.NewFromString("5.01")) // 25.00
// Adding negative (subtraction)
a.Add(decimal.NewFromInt(-10)) // 90
// Zero
a.Add(decimal.Zero) // 100 (unchanged)Use Cases:
// Cart total
subtotal := item1Price.Add(item2Price).Add(item3Price)
// Apply tax
total := subtotal.Add(taxAmount)
// Accumulate values
sum := decimal.Zero
for _, price := range prices {
sum = sum.Add(price)
}func (d Decimal) Sub(d2 Decimal) Decimal
// Returns d - d2Examples:
a := decimal.NewFromInt(100)
b := decimal.NewFromFloat(23.50)
a.Sub(b) // 76.50
decimal.NewFromString("100.00").Sub(
decimal.NewFromString("25.99")) // 74.01
// Negative result
decimal.NewFromInt(10).Sub(decimal.NewFromInt(20)) // -10Use Cases:
// Apply discount
discounted := price.Sub(discountAmount)
// Calculate change
change := payment.Sub(total)
// Price difference
difference := newPrice.Sub(oldPrice)func (d Decimal) Mul(d2 Decimal) Decimal
// Returns d * d2Examples:
price := decimal.NewFromString("19.99")
quantity := decimal.NewFromInt(3)
price.Mul(quantity) // 59.97
// Percentages
decimal.NewFromInt(100).Mul(decimal.NewFromFloat(0.08)) // 8
// By negative
decimal.NewFromInt(10).Mul(decimal.NewFromInt(-1)) // -10Use Cases:
// Item subtotal
subtotal := price.Mul(quantity)
// Calculate tax amount
taxAmount := subtotal.Mul(taxRate)
// Percentage calculation
discount := price.Mul(discountPercent).Div(decimal.NewFromInt(100))
// Scale quantity
scaled := unitPrice.Mul(decimal.NewFromFloat(2.5))func (d Decimal) Div(d2 Decimal) Decimal
// Returns d / d2
// Uses DivisionPrecision (default 16) for non-exact divisions
// PANICS on division by zeroExamples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(3)
a.Div(b) // 3.3333333333333333 (16 digits default)
decimal.NewFromInt(100).Div(decimal.NewFromInt(4)) // 25 (exact)
decimal.NewFromInt(1).Div(decimal.NewFromInt(3)) // 0.3333333333333333IMPORTANT: Division by Zero
// BAD: Will panic
result := a.Div(decimal.Zero)
// GOOD: Check first
if divisor.IsZero() {
return errors.New("division by zero")
}
result := a.Div(divisor)Use Cases:
// Average price
avgPrice := totalPrice.Div(decimal.NewFromInt(itemCount))
// Unit price
unitPrice := totalPrice.Div(quantity)
// Split evenly
perPerson := total.Div(decimal.NewFromInt(numberOfPeople))
// Percentage (e.g., what percent is 25 of 100?)
percent := decimal.NewFromInt(25).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromInt(100)) // 25%Precision Control:
// Default precision (16 digits)
decimal.NewFromInt(1).Div(decimal.NewFromInt(3))
// "0.3333333333333333"
// Increase precision
old := decimal.DivisionPrecision
decimal.DivisionPrecision = 30
result := decimal.NewFromInt(1).Div(decimal.NewFromInt(3))
decimal.DivisionPrecision = old
// "0.333333333333333333333333333333"func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal
// Divides and rounds to precision decimal places
// precision can be negative (rounds integer part)Examples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(3)
a.DivRound(b, 2) // 3.33
a.DivRound(b, 4) // 3.3333
a.DivRound(b, 0) // 3
// Financial calculation (always 2 decimal places)
price := decimal.NewFromString("100.00")
quantity := decimal.NewFromInt(3)
unitPrice := price.DivRound(quantity, 2) // 33.33func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal)
// Returns quotient and remainder
// d = d2 * quotient + remainderExamples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(3)
q, r := a.QuoRem(b, 0)
// q = 3, r = 1
// Verify: 3 * 3 + 1 = 10 ✓
// With precision
q2, r2 := decimal.NewFromString("10.5").QuoRem(
decimal.NewFromInt(3), 1)
// q2 = 3.5, r2 = 0.0Use Cases:
// Distribute amount across accounts
totalAmount := decimal.NewFromString("100.00")
numAccounts := decimal.NewFromInt(3)
amountEach, remainder := totalAmount.QuoRem(numAccounts, 2)
// amountEach = 33.33
// remainder = 0.01
// Give remainder to first accountfunc (d Decimal) Mod(d2 Decimal) Decimal
// Returns d mod d2 (remainder of d / d2)Examples:
decimal.NewFromInt(10).Mod(decimal.NewFromInt(3)) // 1
decimal.NewFromInt(17).Mod(decimal.NewFromInt(5)) // 2
decimal.NewFromFloat(5.5).Mod(decimal.NewFromInt(2)) // 1.5Use Cases:
// Check if divisible
if amount.Mod(minimumUnit).IsZero() {
// Evenly divisible
}
// Wrap around
index := currentIndex.Mod(decimal.NewFromInt(arrayLength))func (d Decimal) Neg() Decimal
// Returns -dExamples:
decimal.NewFromInt(10).Neg() // -10
decimal.NewFromInt(-10).Neg() // 10
decimal.Zero.Neg() // 0Use Cases:
// Reverse transaction
refund := charge.Neg()
// Credit vs debit
if isDebit {
amount = amount.Neg()
}
// Toggle sign
amount = amount.Neg()func (d Decimal) Abs() Decimal
// Returns |d|Examples:
decimal.NewFromInt(-10).Abs() // 10
decimal.NewFromInt(10).Abs() // 10
decimal.Zero.Abs() // 0Use Cases:
// Magnitude regardless of sign
distance := position1.Sub(position2).Abs()
// Total absolute change
totalChange := decimal.Zero
for _, change := range changes {
totalChange = totalChange.Add(change.Abs())
}
// Ensure positive
positiveAmount := userInput.Abs()func (d Decimal) Shift(shift int32) Decimal
// Positive shift: multiply by 10^shift (move decimal right)
// Negative shift: divide by 10^|shift| (move decimal left)Examples:
d := decimal.NewFromInt(10)
d.Shift(2) // 1000 (10 * 10²)
d.Shift(1) // 100 (10 * 10¹)
d.Shift(0) // 10 (unchanged)
d.Shift(-1) // 1 (10 * 10⁻¹)
d.Shift(-2) // 0.1 (10 * 10⁻²)
// From string
decimal.NewFromString("1.23").Shift(2) // 123
decimal.NewFromString("1.23").Shift(-2) // 0.0123Use Cases:
// Convert cents to dollars
cents := decimal.NewFromInt(1999)
dollars := cents.Shift(-2) // 19.99
// Convert dollars to cents
dollars := decimal.NewFromString("19.99")
cents := dollars.Shift(2) // 1999
// Percentage to decimal
percent := decimal.NewFromInt(8)
rate := percent.Shift(-2) // 0.08
// Scale by order of magnitude
scaled := value.Shift(3) // multiply by 1000func Sum(first Decimal, rest ...Decimal) Decimal
// Returns sum of all arguments (requires at least one)Examples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(20)
c := decimal.NewFromInt(30)
decimal.Sum(a, b, c) // 60
decimal.Sum(a) // 10
// From slice
values := []decimal.Decimal{a, b, c}
decimal.Sum(values[0], values[1:]...) // 60Use Cases:
// Cart total
total := decimal.Sum(item1, item2, item3)
// Aggregate from slice
func sumPrices(prices []decimal.Decimal) decimal.Decimal {
if len(prices) == 0 {
return decimal.Zero
}
return decimal.Sum(prices[0], prices[1:]...)
}func Avg(first Decimal, rest ...Decimal) Decimal
// Returns average of all arguments (requires at least one)Examples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(20)
c := decimal.NewFromInt(30)
decimal.Avg(a, b, c) // 20
decimal.Avg(a) // 10Use Cases:
// Average price
avgPrice := decimal.Avg(price1, price2, price3)
// From slice
func avgPrices(prices []decimal.Decimal) decimal.Decimal {
if len(prices) == 0 {
return decimal.Zero
}
return decimal.Avg(prices[0], prices[1:]...)
}func Max(first Decimal, rest ...Decimal) Decimal
// Returns largest value (requires at least one)Examples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(30)
c := decimal.NewFromInt(20)
decimal.Max(a, b, c) // 30
decimal.Max(a) // 10Use Cases:
// Maximum price
maxPrice := decimal.Max(price1, price2, price3)
// Ensure minimum value
adjusted := decimal.Max(calculated, minimumAllowed)func Min(first Decimal, rest ...Decimal) Decimal
// Returns smallest value (requires at least one)Examples:
a := decimal.NewFromInt(10)
b := decimal.NewFromInt(30)
c := decimal.NewFromInt(20)
decimal.Min(a, b, c) // 10
decimal.Min(a) // 10Use Cases:
// Minimum price
minPrice := decimal.Min(price1, price2, price3)
// Enforce maximum value
capped := decimal.Min(calculated, maximumAllowed)
// Apply discount cap
discountAmount := decimal.Min(calculatedDiscount, maxDiscount)// Price with tax
subtotal := price.Mul(quantity)
taxAmount := subtotal.Mul(taxRate)
total := subtotal.Add(taxAmount)
// Apply percentage discount
discountPercent := decimal.NewFromInt(15)
discountAmount := price.Mul(discountPercent).Div(decimal.NewFromInt(100))
finalPrice := price.Sub(discountAmount)
// Rounding for currency
finalPrice = finalPrice.Round(2)// Per-unit price
totalPrice := decimal.NewFromString("19.99")
quantity := decimal.NewFromInt(6)
unitPrice := totalPrice.DivRound(quantity, 2) // 3.33
// Verify total
verifyTotal := unitPrice.Mul(quantity) // 19.98 (may differ by rounding)// Calculate percentage of total
part := decimal.NewFromInt(25)
total := decimal.NewFromInt(100)
percentage := part.Div(total).Mul(decimal.NewFromInt(100)) // 25%
// Apply percentage
total := decimal.NewFromString("100.00")
percent := decimal.NewFromInt(15)
amount := total.Mul(percent).Div(decimal.NewFromInt(100)) // 15.00// Split evenly
total := decimal.NewFromString("100.00")
numPeople := decimal.NewFromInt(3)
perPerson := total.DivRound(numPeople, 2) // 33.33
// Handle remainder
perPerson, remainder := total.QuoRem(numPeople, 2)
// perPerson = 33.33, remainder = 0.01
// Give remainder to first person
firstPersonAmount := perPerson.Add(remainder)// Sum from loop
total := decimal.Zero
for _, item := range items {
total = total.Add(item.Price.Mul(item.Quantity))
}
// Average from loop
sum := decimal.Zero
count := 0
for _, price := range prices {
sum = sum.Add(price)
count++
}
if count > 0 {
avg := sum.Div(decimal.NewFromInt(int64(count)))
}// Convert with exchange rate
amountUSD := decimal.NewFromString("100.00")
exchangeRate := decimal.NewFromFloat(1.3) // USD to EUR
amountEUR := amountUSD.Mul(exchangeRate).Round(2) // 130.00// Check before dividing
if divisor.IsZero() {
return decimal.Zero, errors.New("division by zero")
}
result := dividend.Div(divisor)// Use DivRound for financial calculations
unitPrice := totalPrice.DivRound(quantity, 2) // Always 2 decimals
// Or use Round after operation
result := a.Div(b).Round(2)Avoid unnecessary operations:
// BAD: Unnecessary division then multiplication
result := value.Div(decimal.NewFromInt(100)).Mul(decimal.NewFromInt(100))
// GOOD: No operation needed
result := valueUse Shift for powers of 10:
// FASTER
dollars := cents.Shift(-2)
// SLOWER
dollars := cents.Div(decimal.NewFromInt(100))Reuse common values:
hundred := decimal.NewFromInt(100)
// Reuse hundred multiple times
pct1 := value1.Mul(rate1).Div(hundred)
pct2 := value2.Mul(rate2).Div(hundred)Install with Tessl CLI
npx tessl i tessl/golang-github-com-shopspring--decimal