CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/golang-github-com-shopspring--decimal

Arbitrary-precision fixed-point decimal numbers for Go, avoiding floating-point precision issues with support for arithmetic, rounding, serialization, and database integration.

Overview
Eval results
Files

basic-arithmetic.mddocs/guides/

Basic Arithmetic Guide

Complete guide to arithmetic operations with practical examples.

Quick Reference

OperationMethodExample
AdditionAdd(d2)a.Add(b) → a + b
SubtractionSub(d2)a.Sub(b) → a - b
MultiplicationMul(d2)a.Mul(b) → a * b
DivisionDiv(d2)a.Div(b) → a / b
ModuloMod(d2)a.Mod(b) → a % b
NegationNeg()a.Neg() → -a
AbsoluteAbs()a.Abs() → |a|
ShiftShift(n)a.Shift(2) → a * 10²

Key Concepts

Immutability

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

Chaining Operations

// 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))         // 152

Basic Operations

Addition - Add()

func (d Decimal) Add(d2 Decimal) Decimal
// Returns d + d2

Examples:

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)
}

Subtraction - Sub()

func (d Decimal) Sub(d2 Decimal) Decimal
// Returns d - d2

Examples:

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))  // -10

Use Cases:

// Apply discount
discounted := price.Sub(discountAmount)

// Calculate change
change := payment.Sub(total)

// Price difference
difference := newPrice.Sub(oldPrice)

Multiplication - Mul()

func (d Decimal) Mul(d2 Decimal) Decimal
// Returns d * d2

Examples:

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))       // -10

Use 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))

Division - Div()

func (d Decimal) Div(d2 Decimal) Decimal
// Returns d / d2
// Uses DivisionPrecision (default 16) for non-exact divisions
// PANICS on division by zero

Examples:

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.3333333333333333

IMPORTANT: 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"

Division with Rounding - DivRound()

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.33

Quotient and Remainder - QuoRem()

func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal)
// Returns quotient and remainder
// d = d2 * quotient + remainder

Examples:

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.0

Use 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 account

Modulo - Mod()

func (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.5

Use Cases:

// Check if divisible
if amount.Mod(minimumUnit).IsZero() {
    // Evenly divisible
}

// Wrap around
index := currentIndex.Mod(decimal.NewFromInt(arrayLength))

Negation - Neg()

func (d Decimal) Neg() Decimal
// Returns -d

Examples:

decimal.NewFromInt(10).Neg()      // -10
decimal.NewFromInt(-10).Neg()     // 10
decimal.Zero.Neg()                // 0

Use Cases:

// Reverse transaction
refund := charge.Neg()

// Credit vs debit
if isDebit {
    amount = amount.Neg()
}

// Toggle sign
amount = amount.Neg()

Absolute Value - Abs()

func (d Decimal) Abs() Decimal
// Returns |d|

Examples:

decimal.NewFromInt(-10).Abs()     // 10
decimal.NewFromInt(10).Abs()      // 10
decimal.Zero.Abs()                // 0

Use 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()

Shift - Multiply/Divide by Powers of 10

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.0123

Use 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 1000

Aggregate Functions

Sum

func 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:]...)  // 60

Use 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:]...)
}

Average

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)                    // 10

Use 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:]...)
}

Maximum

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)                    // 10

Use Cases:

// Maximum price
maxPrice := decimal.Max(price1, price2, price3)

// Ensure minimum value
adjusted := decimal.Max(calculated, minimumAllowed)

Minimum

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)                    // 10

Use 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)

Common Patterns

Financial Calculations

// 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)

Unit Price Calculation

// 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)

Percentage Calculations

// 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

Splitting Amounts

// 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)

Accumulation

// 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)))
}

Currency Conversion

// 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

Error Handling

Division by Zero

// Check before dividing
if divisor.IsZero() {
    return decimal.Zero, errors.New("division by zero")
}
result := dividend.Div(divisor)

Precision Loss

// Use DivRound for financial calculations
unitPrice := totalPrice.DivRound(quantity, 2)  // Always 2 decimals

// Or use Round after operation
result := a.Div(b).Round(2)

Performance Tips

  1. Avoid unnecessary operations:

    // BAD: Unnecessary division then multiplication
    result := value.Div(decimal.NewFromInt(100)).Mul(decimal.NewFromInt(100))
    
    // GOOD: No operation needed
    result := value
  2. Use Shift for powers of 10:

    // FASTER
    dollars := cents.Shift(-2)
    
    // SLOWER
    dollars := cents.Div(decimal.NewFromInt(100))
  3. Reuse common values:

    hundred := decimal.NewFromInt(100)
    
    // Reuse hundred multiple times
    pct1 := value1.Mul(rate1).Div(hundred)
    pct2 := value2.Mul(rate2).Div(hundred)

Complete Arithmetic API Reference →

Install with Tessl CLI

npx tessl i tessl/golang-github-com-shopspring--decimal@1.4.1

docs

index.md

README.md

tile.json