Skip to content

TimLai666/go-decimal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-decimal

Fixed-point decimal arithmetic with a configurable Context (scale + rounding) and a compile-once expression engine.

Features

  • Fixed-point decimal core: store scaled integers to avoid binary float drift
  • Context controls output scale and rounding mode
  • Math functions: Sqrt, Exp, Log, Pow, Cmp
  • Expression compiler: tokenize + shunting-yard + RPN VM (compile once, eval fast)
  • ^ operator in expressions for exponentiation

Install

go get github.com/TimLai666/go-decimal

Decimal usage

ctx := decimal.Context{Scale: 2, Mode: decimal.RoundingModeHalfUp}

price := decimal.MustParse(ctx, "12.345") // 12.35
qty := decimal.MustParse(ctx, "2")

subtotal := decimal.Mul(ctx, price, qty) // 24.70

discount := decimal.MustParse(ctx, "1.01")

final := decimal.Sub(ctx, subtotal, discount)
fmt.Println(final.String()) // 23.69

Rounding modes

Listed in roughly decreasing order of typical use. RoundingModeHalfUp is the zero value, so Context{} rounds half away from zero by default.

Mode Direction 1.25 → 1dp -1.25 → 1dp
RoundingModeHalfUp halves away from zero (default) 1.3 -1.3
RoundingModeHalfEven halves to even (banker's rounding) 1.2 -1.2
RoundingModeDown toward zero 1.2 -1.2
RoundingModeUp away from zero 1.3 -1.3
RoundingModeCeiling toward +∞ 1.3 -1.2
RoundingModeFloor toward −∞ 1.2 -1.3
RoundingModeHalfDown halves toward zero 1.2 -1.2
RoundingMode05Up step iff last kept digit is 0 or 5 1.2 -1.2
RoundingModeUnnecessary assert no rounding; panic if any is required panic panic

Compatibility:

  • HalfUp / HalfDown / HalfEven match Java BigDecimal.ROUND_HALF_* and Python decimal.ROUND_HALF_*.
  • Ceiling / Floor / Down correspond to IEEE 754 roundToward{Positive,Negative,Zero}.
  • HalfEven is the IEEE 754 default and Python decimal's default — prefer it for long sums to avoid the upward bias that HalfUp accumulates.
  • 05Up matches Python's decimal.ROUND_05UP, an accounting-oriented rule that avoids producing 5-ending digits unless they are exact.
  • Unnecessary mirrors Java's RoundingMode.UNNECESSARY. When rounding would actually be required, the operation panics with ErrRoundingNecessary; recover with errors.Is(r.(error), decimal.ErrRoundingNecessary).

All non-panicking operations normalize to Context.Scale.

Math functions

ctx := decimal.Context{Scale: 10, Mode: decimal.RoundingModeHalfUp}

decimal.Sqrt(ctx, decimal.MustParse(ctx, "2"))   // 1.4142135624
decimal.Exp(ctx, decimal.MustParse(ctx, "1"))    // 2.7182818285
decimal.Log(ctx, decimal.MustParse(ctx, "10"))   // 2.3025850930
decimal.Pow(ctx, decimal.MustParse(ctx, "2"),
                 decimal.MustParse(ctx, "10"))   // 1024.0000000000

Pow switches strategy automatically: integer exponents use square-and-multiply (exact, supports negative bases), non-integer exponents go through Exp(exp · Log(base)) and require a positive base. Errors are returned for Sqrt(<0), Log(≤0), Pow(<0, fractional), and Pow(0, <0).

Expression usage

ctx := decimal.Context{Scale: 2, Mode: decimal.RoundingModeHalfUp}

prog, err := expr.Compile("1.2 + x/3 + x^2")
if err != nil {
    log.Fatal(err)
}

vars := expr.MapVars{
    "x": decimal.MustParse(ctx, "10"),
}

res, err := prog.Eval(ctx, vars)
if err != nil {
    log.Fatal(err)
}

fmt.Println(res.String()) // 104.53

Operators recognized by expr.Compile: +, -, *, /, ^, plus unary +/- and parentheses. ^ is right-associative and binds tighter than * and /, matching Python: 2^3^2 == 512, -2^2 == -4, (-2)^2 == 4.

Notes

  • Fixed-point decimals: value = int / 10^scale
  • Division is integer division with rounding to Context.Scale
  • No scientific notation in literals (1e5 is not accepted)

Benchmarks

go test ./... -bench=. -benchmem

About

Fixed-point decimal arithmetic with a configurable Context (scale + rounding) and a compile-once expression engine.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages