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 formatting Decimal values as strings and applying rounding strategies.
| Method | Output | Example Input → Output |
|---|---|---|
String() | Full precision | 123.4567 → "123.4567" |
StringFixed(2) | Fixed places (half-up) | 123.456 → "123.46" |
StringFixedBank(2) | Fixed places (banker's) | 123.455 → "123.46" |
StringFixedCash(5) | Cash rounding | 3.43 → "3.45" |
| Method | Strategy | 5.45 at 1 place | -5.45 at 1 place |
|---|---|---|---|
Round(1) | Half-up (away from 0) | 5.5 | -5.5 |
RoundBank(1) | Banker's (to even) | 5.4 | -5.4 |
RoundUp(1) | Away from zero | 5.5 | -5.5 |
RoundDown(1) | Towards zero | 5.4 | -5.4 |
RoundCeil(1) | Towards +∞ | 5.5 | -5.4 |
RoundFloor(1) | Towards -∞ | 5.4 | -5.5 |
Truncate(1) | Cut off digits | 5.4 | -5.4 |
Need to round a decimal?
│
├─ Financial/Currency? → Round(2) or RoundBank(2)
│ └─ Fair rounding? → RoundBank(2) (banker's rounding)
│
├─ Cash transactions? → RoundCash(5, 10, 25, 50, or 100)
│
├─ Always round up (ceiling)? → RoundCeil(places)
│
├─ Always round down (floor)? → RoundFloor(places)
│
├─ Towards zero? → RoundDown(places) or Truncate(precision)
│
├─ Away from zero? → RoundUp(places)
│
└─ Standard rounding? → Round(places) (half-up)func (d Decimal) String() string
// Returns string with full precision, no roundingExamples:
decimal.NewFromString("123.4567").String() // "123.4567"
decimal.NewFromFloat(1234.5678).String() // "1234.5678"
decimal.NewFromInt(100).String() // "100"
decimal.New(-12345, -3).String() // "-12.345"Use when:
func (d Decimal) StringFixed(places int32) string
// Returns string with fixed places after decimal, half-up rounding
// Negative places rounds integer partExamples:
d := decimal.NewFromFloat(1234.5678)
d.StringFixed(2) // "1234.57"
d.StringFixed(0) // "1235"
d.StringFixed(6) // "1234.567800" (pads with zeros)
d.StringFixed(-1) // "1230" (rounds to tens)
d.StringFixed(-2) // "1200" (rounds to hundreds)
// Edge cases
decimal.NewFromFloat(5.45).StringFixed(1) // "5.5" (half-up)
decimal.NewFromFloat(5.44).StringFixed(1) // "5.4"Use cases:
// Currency display (always 2 decimals)
fmt.Printf("$%s", price.StringFixed(2)) // "$19.99"
// Percentage display
fmt.Printf("%s%%", percent.StringFixed(1)) // "15.5%"
// Rounded output
fmt.Println(result.StringFixed(2))func (d Decimal) StringFixedBank(places int32) string
// Returns string with fixed places, banker's rounding (half-to-even)
// Negative places rounds integer partBanker's Rounding:
Examples:
// Half-to-even rounding
decimal.NewFromFloat(5.45).StringFixedBank(1) // "5.4" (rounds to even: 4)
decimal.NewFromFloat(5.55).StringFixedBank(1) // "5.6" (rounds to even: 6)
decimal.NewFromFloat(5.46).StringFixedBank(1) // "5.5" (not exactly 5)
// Integer rounding
decimal.NewFromInt(545).StringFixedBank(-1) // "540" (rounds to even: 4)
decimal.NewFromInt(555).StringFixedBank(-1) // "560" (rounds to even: 6)Use when:
func (d Decimal) StringFixedCash(interval uint8) string
// Rounds to nearest multiple of cash interval
// interval must be: 5, 10, 25, 50, or 100 (cents)
// Panics for other valuesIntervals:
5 = nickel (5¢) rounding10 = dime (10¢) rounding25 = quarter (25¢) rounding50 = half-dollar (50¢) rounding100 = dollar ($1) roundingExamples:
// Nickel rounding (5 cents)
decimal.NewFromFloat(3.43).StringFixedCash(5) // "3.45"
decimal.NewFromFloat(3.47).StringFixedCash(5) // "3.45"
decimal.NewFromFloat(3.48).StringFixedCash(5) // "3.50"
// Dime rounding (10 cents)
decimal.NewFromFloat(3.45).StringFixedCash(10) // "3.50"
decimal.NewFromFloat(3.44).StringFixedCash(10) // "3.40"
// Quarter rounding (25 cents)
decimal.NewFromFloat(3.41).StringFixedCash(25) // "3.50"
decimal.NewFromFloat(3.37).StringFixedCash(25) // "3.25"
// Half-dollar rounding (50 cents)
decimal.NewFromFloat(3.75).StringFixedCash(50) // "4.00"
decimal.NewFromFloat(3.24).StringFixedCash(50) // "3.00"
// Dollar rounding (100 cents)
decimal.NewFromFloat(3.50).StringFixedCash(100) // "4.00"
decimal.NewFromFloat(3.49).StringFixedCash(100) // "3.00"Use cases:
// Point of sale (no pennies)
total := calculateTotal()
displayAmount := total.StringFixedCash(5)
// Cash payment (round to nickel in some countries)
cashAmount, _ := decimal.NewFromString(total.StringFixedCash(5))All rounding methods return new Decimal values (immutable).
func (d Decimal) Round(places int32) Decimal
// Rounds to places decimal places using half-up strategy
// When digit is 5, rounds away from zeroExamples:
decimal.NewFromFloat(5.45).Round(1) // 5.5
decimal.NewFromFloat(5.44).Round(1) // 5.4
decimal.NewFromFloat(5.445).Round(2) // 5.45
decimal.NewFromFloat(-5.45).Round(1) // -5.5 (away from zero)
// Integer rounding (negative places)
decimal.NewFromInt(545).Round(-1) // 550 (round to tens)
decimal.NewFromInt(544).Round(-1) // 540
decimal.NewFromInt(545).Round(-2) // 500 (round to hundreds)Use cases:
// Standard financial rounding
price := calculatePrice().Round(2)
// Round percentage
percentage := calculated.Round(1)func (d Decimal) RoundBank(places int32) Decimal
// Rounds using banker's rounding (half-to-even)
// When digit is exactly 5, rounds to nearest evenExamples:
// Half-to-even
decimal.NewFromFloat(5.45).RoundBank(1) // 5.4 (to even)
decimal.NewFromFloat(5.55).RoundBank(1) // 5.6 (to even)
decimal.NewFromFloat(5.35).RoundBank(1) // 5.4 (to even)
// Not exactly 5
decimal.NewFromFloat(5.46).RoundBank(1) // 5.5 (normal rounding)
// Integer rounding
decimal.NewFromInt(545).RoundBank(-1) // 540 (to even)
decimal.NewFromInt(555).RoundBank(-1) // 560 (to even)Use cases:
// Financial calculations where bias matters
result := calculation.RoundBank(2)
// Compliance with regulations requiring banker's rounding
taxAmount := amount.Mul(rate).RoundBank(2)func (d Decimal) RoundUp(places int32) Decimal
// Always rounds away from zero
// Positive: ceiling, Negative: floorExamples:
decimal.NewFromFloat(5.41).RoundUp(1) // 5.5
decimal.NewFromFloat(5.40).RoundUp(1) // 5.4 (no change)
decimal.NewFromFloat(-5.41).RoundUp(1) // -5.5 (away from zero)
// Integer rounding
decimal.NewFromInt(541).RoundUp(-2) // 600
decimal.NewFromInt(500).RoundUp(-2) // 500 (no change)Use cases:
// Conservative estimates (ensure enough)
bufferAmount := estimate.RoundUp(2)
// Allocate resources (round up to ensure coverage)
requiredUnits := calculated.RoundUp(0)func (d Decimal) RoundDown(places int32) Decimal
// Always rounds towards zero (truncation)
// Positive: floor, Negative: ceilingExamples:
decimal.NewFromFloat(5.49).RoundDown(1) // 5.4
decimal.NewFromFloat(-5.49).RoundDown(1) // -5.4 (towards zero)
// Integer rounding
decimal.NewFromInt(549).RoundDown(-2) // 500Use cases:
// Pessimistic estimates
minAmount := estimate.RoundDown(2)
// Truncate fractional parts
wholeUnits := amount.RoundDown(0)func (d Decimal) RoundCeil(places int32) Decimal
// Always rounds towards +∞ (ceiling)Examples:
decimal.NewFromFloat(5.41).RoundCeil(1) // 5.5 (up)
decimal.NewFromFloat(5.40).RoundCeil(1) // 5.4 (no change)
decimal.NewFromFloat(-5.41).RoundCeil(1) // -5.4 (up towards +∞)
// Integer rounding
decimal.NewFromInt(541).RoundCeil(-2) // 600
decimal.NewFromInt(-541).RoundCeil(-2) // -500Use cases:
// Always charge at least minimum
chargeAmount := calculated.RoundCeil(2)
// Ensure positive minimum
positiveMin := value.RoundCeil(0) // at least 1 if > 0func (d Decimal) RoundFloor(places int32) Decimal
// Always rounds towards -∞ (floor)Examples:
decimal.NewFromFloat(5.49).RoundFloor(1) // 5.4 (down)
decimal.NewFromFloat(-5.41).RoundFloor(1) // -5.5 (down towards -∞)
// Integer rounding
decimal.NewFromInt(549).RoundFloor(-2) // 500
decimal.NewFromInt(-541).RoundFloor(-2) // -600Use cases:
// Always give customer benefit of doubt
discount := calculated.RoundFloor(2)
// Ensure doesn't exceed limit
cappedAmount := amount.RoundFloor(2)func (d Decimal) Truncate(precision int32) Decimal
// Removes digits beyond precision without rounding
// precision is last digit kept (must be >= 0)Examples:
d := decimal.NewFromString("123.456")
d.Truncate(2) // "123.45" (keep 2 decimal places)
d.Truncate(1) // "123.4" (keep 1 decimal place)
d.Truncate(0) // "123" (no decimal places)
// No negative precision (unlike Round methods)Use cases:
// Display without rounding
display := value.Truncate(2).String()
// Remove fractional part
integer := value.Truncate(0)func (d Decimal) RoundCash(interval uint8) Decimal
// Rounds to nearest cash interval
// interval must be: 5, 10, 25, 50, or 100
// Returns Decimal (not string)Examples:
decimal.NewFromFloat(3.43).RoundCash(5) // 3.45
decimal.NewFromFloat(3.43).RoundCash(10) // 3.40
decimal.NewFromFloat(3.43).RoundCash(25) // 3.50Use cases:
// Calculate cash payment amount
cashTotal := total.RoundCash(5)
// Store rounded value for later
rounded := amount.RoundCash(5)
db.Exec("INSERT INTO transactions (amount) VALUES (?)", rounded)func (d Decimal) Floor() Decimal
// Returns nearest integer <= d
func (d Decimal) Ceil() Decimal
// Returns nearest integer >= dExamples:
// Floor
decimal.NewFromFloat(5.9).Floor() // 5
decimal.NewFromFloat(-5.1).Floor() // -6 (down to -∞)
decimal.NewFromInt(5).Floor() // 5 (no change)
// Ceil
decimal.NewFromFloat(5.1).Ceil() // 6
decimal.NewFromFloat(-5.9).Ceil() // -5 (up to +∞)
decimal.NewFromInt(5).Ceil() // 5 (no change)Use cases:
// Count whole items (round up)
itemsNeeded := calculated.Ceil()
// Count completed items (round down)
itemsCompleted := calculated.Floor()// Always show 2 decimal places
func formatCurrency(amount decimal.Decimal) string {
return "$" + amount.StringFixed(2)
}
// Usage
fmt.Println(formatCurrency(decimal.NewFromString("19.99"))) // "$19.99"
fmt.Println(formatCurrency(decimal.NewFromInt(20))) // "$20.00"// Show percentage with 1 decimal place
func formatPercent(rate decimal.Decimal) string {
percent := rate.Mul(decimal.NewFromInt(100))
return percent.StringFixed(1) + "%"
}
// Usage
rate := decimal.NewFromFloat(0.085)
fmt.Println(formatPercent(rate)) // "8.5%"// Calculate tax (round to 2 places)
subtotal := decimal.NewFromString("99.99")
taxRate := decimal.NewFromFloat(0.08)
taxAmount := subtotal.Mul(taxRate).Round(2) // 8.00
// Total
total := subtotal.Add(taxAmount) // 107.99// Cash transaction (round to nickel)
total := calculateTotal()
cashAmount := total.RoundCash(5)
fmt.Printf("Total: %s\n", total.StringFixed(2))
fmt.Printf("Cash: %s\n", cashAmount.StringFixed(2))// Process many transactions with fair rounding
var totalRounded decimal.Decimal
for _, amount := range amounts {
rounded := amount.RoundBank(2) // Fair rounding
totalRounded = totalRounded.Add(rounded)
}// Round just before showing to user
displayPrice := price.StringFixed(2)// Calculate first, then round final result
result := a.Mul(b).Add(c).Div(d).Round(2)// Round before storing in database
rounded := calculated.Round(2)
db.Exec("INSERT INTO amounts (value) VALUES (?)", rounded)// Round intermediate results for financial precision
taxAmount := subtotal.Mul(taxRate).Round(2) // Round tax
discountAmount := subtotal.Mul(discountRate).Round(2) // Round discount
total := subtotal.Add(taxAmount).Sub(discountAmount) // No rounding neededValue: 5.455
Round(2) → 5.46 (half-up)
RoundBank(2) → 5.46 (to even)
RoundUp(2) → 5.46 (away from zero)
RoundDown(2) → 5.45 (towards zero)
RoundCeil(2) → 5.46 (towards +∞)
RoundFloor(2) → 5.45 (towards -∞)
Truncate(2) → 5.45 (cut off)
Value: 5.445
Round(2) → 5.45 (half-up, but floating point!)
RoundBank(2) → 5.44 (to even)
RoundUp(2) → 5.45 (away from zero)
RoundDown(2) → 5.44 (towards zero)
RoundCeil(2) → 5.45 (towards +∞)
RoundFloor(2) → 5.44 (towards -∞)
Truncate(2) → 5.44 (cut off)// Default: 16 decimal places
decimal.NewFromInt(1).Div(decimal.NewFromInt(3))
// "0.3333333333333333"
// Increase precision
decimal.DivisionPrecision = 4
decimal.NewFromInt(1).Div(decimal.NewFromInt(3))
// "0.3333"
// Restore default
decimal.DivisionPrecision = 16Use cases:
// Financial application (2 decimal places)
decimal.DivisionPrecision = 2
unitPrice := totalPrice.Div(quantity) // Always 2 decimals
// Scientific calculation (30 decimal places)
decimal.DivisionPrecision = 30
precise := a.Div(b)Install with Tessl CLI
npx tessl i tessl/golang-github-com-shopspring--decimal@1.4.1