qdecimal

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 16 Imported by: 0

README

qdecimal

Exact base-10 decimal arithmetic for finance, banking, exchanges, ledgers, and trading systems.

qdecimal is designed around one rule: money arithmetic must be explicit, deterministic, and boring in production. It does not use package-global division precision, it does not silently accept NaN or infinity, and every operation that can discard digits requires a caller-provided scale and rounding mode.

Install

go get github.com/MeViksry/qdecimal

Quick Start

package main

import (
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	price := qdecimal.MustParse("123.4500")
	size := qdecimal.MustParse("0.25")

	notional := price.Mul(size)
	rounded, err := notional.Round(2, qdecimal.ToNearestEven)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(rounded) // 30.86
}

Design Principles

  • Exact decimal model: values are stored as coefficient * 10^-scale.
  • No global mutable precision: division and rescaling require explicit scale and rounding mode.
  • No hidden float behavior: FromFloat64 rejects NaN and infinity and is documented as an integration boundary, not a finance input path.
  • Scale preserving: 0.00, 1.20, and 123.4500 can be represented and serialized exactly.
  • No negative zero: -0.00 becomes 0.00 while preserving scale.
  • Immutable API: operations never mutate their receiver.
  • Currency-safe money: Money rejects arithmetic across different currency or asset codes.
  • Fixed64 hot path: bounded-scale int64 decimal for tight ledger and trading loops.
  • Safe JSON default: JSON marshaling emits strings, not lossy floating-point numbers.
  • SQL ready: Decimal and NullDecimal implement scanner and valuer interfaces.
  • Formatter ready: Decimal, Fixed64, and Money implement fmt.Formatter with width, sign, quote, and fixed-precision handling.
  • Portable binary format: versioned binary marshaling for file and wire storage.

Rounding

amount := qdecimal.MustParse("-1.25")
rounded, err := amount.Round(1, qdecimal.ToNearestAway)

Supported modes:

Mode Meaning
ToNearestEven banker's rounding
ToNearestAway half-up / ties away from zero
ToNearestTowardZero half-down / ties toward zero
AwayFromZero round up by magnitude
TowardZero truncate
TowardPositive ceiling
TowardNegative floor

Aliases are provided for common finance names: RoundBankers, RoundHalfUp, RoundHalfDown, RoundUp, RoundDown, RoundCeil, and RoundFloor.

Rounding modes also marshal as stable audit-friendly strings such as "to_nearest_even" and can be parsed from common aliases:

mode, err := qdecimal.ParseRoundingMode("half-up")

Finance Context

Use Context to carry scale and rounding policy explicitly through services:

usd := qdecimal.MustContext(2, qdecimal.ToNearestEven)

fee, err := usd.Mul(
	qdecimal.MustParse("123.4567"),
	qdecimal.MustParse("0.0025"),
)

There is no package-global precision variable. Every lossy boundary is local and auditable.

Context and MoneyContext marshal as strict lower-case JSON policy objects:

{"currency":"USD","scale":2,"rounding":"to_nearest_even"}

Unknown fields are rejected on decode, currency codes are normalized, and rounding aliases such as "half-up" are accepted.

Minor Units

Ledger systems often store integer minor units:

amount, err := qdecimal.NewFromMinorUnits(12345, 2) // 123.45
cents, err := amount.Int64MinorUnits(2, qdecimal.ToNearestEven)
exactCents, err := amount.Int64MinorUnitsExact(2)
err = qdecimal.ValidateMinorScale(2)

Overflow is reported as ErrOverflow.

Fixed64 Hot Path

Use Fixed64 when a domain has a known scale and the value must fit in signed 64-bit minor units, such as cents, ticks, lots, basis points, or bounded ledger buckets:

price, err := qdecimal.ParseFixed64("123.456", 2, qdecimal.ToNearestAway) // 123.46
fee, err := qdecimal.NewFixed64(25, 4)                                    // 0.0025
notional, err := price.Mul(fee, 6, qdecimal.ToNearestEven)

Fixed64 supports text, JSON, SQL scanner/valuer, scale alignment, checked addition/subtraction, rounding, multiplication, division, exchange tick/lot quantization, range checks, clamping, min/max helpers, and exact conversion to Decimal. AppendText writes fixed-scale text into caller-owned buffers for hot logging and message paths. All integer overflow is reported as ErrOverflow.

Currency-Safe Money

Use Money when the decimal amount must carry a currency or exchange asset code:

usd, err := qdecimal.NewMoney(qdecimal.MustParse("10.00"), "usd")
usd, err = qdecimal.ParseMoney("USD 10.00")
fee, err := usd.Mul(qdecimal.MustParse("0.0025"), 4, qdecimal.ToNearestEven)
parts, err := usd.Allocate(3, 2, qdecimal.ToNearestEven)

Use MoneyContext to carry a reusable policy for one currency or asset:

usd := qdecimal.MustMoneyContext("USD", 2, qdecimal.ToNearestEven)
btc := qdecimal.MustMoneyContext("BTC", 8, qdecimal.TowardZero)

amount, err := usd.Parse("123.456")
sats, err := btc.FromMinorUnits(123456789)
adjustment, err := usd.Parse("0.01")
total, err := usd.Add(amount, adjustment)
batchTotal, err := usd.Sum(amount, adjustment)

Money normalizes codes such as usd to USD, rejects malformed codes, and returns ErrCurrencyMismatch if callers try to add or compare different currencies. SumMoney, AvgMoney, Money.Between, Money.Clamp, MinMoney, and MaxMoney keep aggregates, range checks, and limits currency-safe. MoneyContext adds policy-aware Sum, Avg, Between, Clamp, Min, Max, and tick-size helpers that return values at the configured scale. Money.QuantizeStepExact and MoneyContext.QuantizeStepExact reject invalid exchange increments with ErrInexact. Allocation preserves the rounded total exactly at the requested minor-unit scale.

Money also implements SQL scanner/valuer using the canonical text format CODE amount, such as USD 123.45. NullMoney handles SQL NULL and JSON null for nullable money fields and also supports text/formatter integration.

Money.Key() and Fixed64.Key() provide canonical comparable keys for maps. fmt precision works for reporting, for example fmt.Sprintf("%.2f", amount). Fixed64.QuantizeStep, SumFixed64, AvgFixed64, and AvgFixed64Exact provide checked tick-size and aggregate helpers for bounded-scale hot paths, falling back to Decimal when alignment or overflow requires arbitrary precision.

qdecimal intentionally does not ship a hard-coded ISO-4217 minor-unit table. Banking and exchange systems should keep currency metadata in their own audited configuration and pass scale/rounding explicitly at lossy boundaries.

Exact-Only Boundaries and Aggregates

Use DivExact when rounding is not allowed:

exact, err := qdecimal.One.DivExact(qdecimal.MustParse("8")) // 0.125
_, err = qdecimal.One.DivExact(qdecimal.MustParse("3"))      // ErrInexact

Use RescaleExact, QuantizeExact, or context *Exact methods when a value must already fit a ledger scale or exchange step:

amount, err := qdecimal.MustParse("123.4500").RescaleExact(2) // 123.45
_, err = qdecimal.MustParse("123.451").RescaleExact(2)        // ErrInexact
exact, err = qdecimal.MustContext(2, qdecimal.ToNearestEven).
	AddExact(qdecimal.MustParse("1.20"), qdecimal.MustParse("0.030"))
exactTick, err := qdecimal.MustContext(2, qdecimal.ToNearestEven).
	QuantizeStepExact(qdecimal.MustParse("1.20"), qdecimal.MustParse("0.05"))
moneyTick, err := qdecimal.MustMoneyContext("USD", 2, qdecimal.ToNearestEven).
	QuantizeStepExact(qdecimal.MustParseMoney("USD 1.20"), qdecimal.MustParse("0.05"))

Aggregates are explicit too:

total := qdecimal.Sum(amounts...)
avg, err := qdecimal.Avg(amounts, 2, qdecimal.ToNearestEven)
exactAvg, err := qdecimal.AvgExact(amounts)
ledgerTotal, err := qdecimal.MustContext(2, qdecimal.ToNearestEven).SumExact(amounts...)
ledgerAvg, err := qdecimal.MustContext(2, qdecimal.ToNearestEven).AvgExact(amounts...)
moneyTotal, err := qdecimal.SumMoney(orders...)
moneyAvg, err := qdecimal.AvgMoney(orders, 2, qdecimal.ToNearestEven)
fixedTotal, err := qdecimal.SumFixed64(fills...)
fixedAvg, err := qdecimal.AvgFixed64(fills, 8, qdecimal.ToNearestEven)

Exchange Tick Sizes

Use QuantizeStep for instruments whose valid increments are not just decimal places:

price := qdecimal.MustParse("1.23")
tick := qdecimal.MustParse("0.05")

rounded, err := price.QuantizeStep(tick, qdecimal.ToNearestAway) // 1.25
policyRounded, err := qdecimal.MustContext(2, qdecimal.ToNearestEven).
	QuantizeStep(qdecimal.MustParse("1.234"), qdecimal.MustParse("0.005"))

Parsing

Strict parser:

d, err := qdecimal.Parse("123456.7890")
d, err = qdecimal.NewFromString("123456.7890") // compatibility alias

Flexible parser:

d, err := qdecimal.ParseFlexible(" 1,234,567.89 ")

Custom separators:

opts := qdecimal.DefaultParseOptions
opts.DecimalSeparator = ','
opts.ThousandsSeparator = '.'
opts.AllowThousands = true

d, err := qdecimal.ParseWithOptions("1.234,50", opts)

Parse accepts the Unicode minus sign () by default and rejects NaN, infinity, malformed thousands groups, and ambiguous syntax. Default parsing also enforces DefaultMaxParseDigits, DefaultMaxParseScale (4096 each), and DefaultMaxParseExponentDigits (10) so hostile exponents or scale-heavy payloads cannot force unbounded coefficient expansion. Set ParseOptions.MaxDigits, ParseOptions.MaxScale, or ParseOptions.MaxExponentDigits to a larger value, or 0 to disable that specific limit, only at trusted boundaries.

NewFromFloat is available as a compatibility alias for FromFloat64, but it returns an error for NaN/infinity and should stay at integration boundaries. Use NewFromFloatWithScale only with an explicit scale and rounding mode.

JSON and SQL

Decimal marshals as a JSON string to avoid precision loss:

"123.4500"

Unmarshal accepts both strings and numeric JSON tokens. When a system explicitly requires numeric JSON output and preserves arbitrary precision end to end, use:

data, err := amount.MarshalJSONWithMode(qdecimal.EmitJSONNumber)
data, err = json.Marshal(qdecimal.AsNumber(amount))

SQL values are written as canonical decimal text. NullDecimal, NullFixed64, and NullMoney handle SQL NULL, JSON null, text "null", and formatter output for nullable integration boundaries.

Scanners accept canonical text/bytes, exact integer source types, and json.Number. Floating-point scanner sources are rejected by default so precision loss stays explicit at integration boundaries.

For optional JSON fields, use pointer fields with omitempty when omission is required:

type Quote struct {
	Price *qdecimal.Decimal `json:"price,omitempty"`
}

Use NullDecimal, NullFixed64, and NullMoney when the wire format should contain explicit null. Non-pointer struct values that implement json.Marshaler are not omitted by Go's omitempty behavior; qdecimal provides IsZero methods for nullable wrappers and tooling that honors zero-value semantics.

Binary and Exact Interop

Decimal, Fixed64, and Money implement binary marshal/unmarshal with versioned, network-order formats. The same stable binary representation is used for GobEncode/GobDecode, which makes cache snapshots and Go-native message payloads deterministic.

binary, err := amount.MarshalBinary()
rat := amount.Rat()

For high-throughput services that reuse buffers, BinarySize and AppendBinary avoid per-message allocations:

buf := make([]byte, 0, amount.BinarySize())
buf, err = amount.AppendBinary(buf)

Decimal, Fixed64, and Money also expose AppendText for canonical text serialization into reusable buffers.

fmt formatting supports %s, %v, %f, %F, and %q. Fixed precision such as %.2f uses banker's rounding and is capped by DefaultMaxFormatScale for untrusted format strings.

Binary and gob decoders use DefaultBinaryDecodeOptions(), which bounds coefficient bytes and scale for untrusted payloads. Trusted file/cache readers can call UnmarshalBinaryWithOptions and raise or disable a specific limit. Length fields are validated before slice conversion so malformed payloads behave consistently across supported CPU architectures.

FromRat and FromBigFloat require explicit scale and rounding mode; FromBigFloat rejects infinity with ErrNonFiniteFloat. PowInt preserves exact natural scale for non-negative integer powers; Pow accepts an integer-valued Decimal exponent and rounds through an explicit scale/mode. Fractional exponents return ErrInexact instead of using hidden floating-point approximations.

Document Database Boundaries

For MongoDB-style Decimal128 Extended JSON without adding a driver dependency:

data, err := json.Marshal(qdecimal.AsExtendedJSON(amount))

This emits:

{"$numberDecimal":"123.4500"}

For raw BSON/document-store boundaries without pulling a driver into the core module, use the dependency-free BSON string helpers:

doc, err := amount.MarshalBSONDocument("amount")
err = amount.UnmarshalBSONDocument(doc, "amount")

These helpers store the decimal as canonical text inside a BSON string field so scale and arbitrary precision are preserved beyond Decimal128's finite range. Oversized BSON decimal text is rejected before it is copied into a Go string. Declared BSON lengths are validated with architecture-neutral arithmetic.

Security Statement

Decimal arithmetic is not cryptography and cannot be made "quantum-resistant" in the cryptographic sense. qdecimal focuses on security properties that matter for finance software:

  • deterministic exact arithmetic;
  • no NaN/infinity propagation;
  • no package-global precision races;
  • no panics from normal input constructors;
  • no third-party Go module dependency in the core package;
  • explicit rounding at every lossy boundary;
  • fuzz, race, stress, and benchmark coverage.

Shopspring Issue-Class Hardening

The test suite includes regression coverage for these issue classes:

  • inconsistent rounding and rescaling;
  • negative rounding and division sign errors;
  • global division precision;
  • JSON string-vs-number safety;
  • configurable JSON number emission without global state;
  • SQL scanner behavior;
  • MongoDB-style Extended JSON boundary;
  • dependency-free BSON string/document boundary;
  • Unicode minus and custom decimal/thousands separators;
  • negative zero;
  • non-finite float inputs;
  • fmt.Formatter support for decimal, fixed, and money values;
  • map-key usage through canonical Key helpers;
  • portable binary serialization;
  • fuzz-tested parser and binary decode boundaries;
  • exact big.Rat conversion and safe integer-exponent powers;
  • exact-only division with ErrInexact;
  • bounded-scale Fixed64 hot path with checked overflow;
  • currency-safe Money arithmetic and allocation;
  • aggregate sum and average helpers;
  • exchange tick-size quantization;
  • min, max, clamp, and between helpers;
  • concurrent immutable use.

Verification

make check
make deps
make coverage
make audit
make stress
make fuzz-smoke
make bench-smoke
make cross-build
make vuln
make consumer-smoke
go test -run '^$' -bench . -benchmem ./...
go test -fuzz='^FuzzParse$' -fuzztime=30s .
go test -fuzz='^FuzzDecimalBinary$' -fuzztime=30s .
go test -fuzz='^FuzzFixed64Binary$' -fuzztime=30s .
go test -fuzz='^FuzzMoneyBinary$' -fuzztime=30s .
go test -fuzz='^FuzzDecimalBSONDocument$' -fuzztime=30s .

make deps fails if any external Go module appears in go list -m all. make coverage fails below COVERAGE_MIN percent total statement coverage (85.0 by default). make stress runs deterministic property and concurrency tests with QDECIMAL_STRESS=1. make bench-smoke executes representative parser, arithmetic, Fixed64, Money, power, and append-binary benchmarks so hot paths keep compiling and publishing cannot drift away from the benchmark suite. make cross-build compile-checks qdecimal and its checked-in release helper for Linux, Windows, macOS, BSD targets, and amd64/386/arm/arm64 class devices without requiring separate physical runners for every target. The normal test suite runs smaller versions of the same stress checks so CI catches regressions quickly, while self-hosted release gates exercise the heavier profile.

See HARDENING.md for the issue-class coverage matrix and audit evidence.

Releases

In this repository, qdecimal releases use the self-hosted qdecimal Release workflow. Pushes to main publish nightly; versioned releases use Go-native tags such as v0.1.0. See RELEASE.md.

Go libraries are not normally published through GitHub Packages. qdecimal is published the standard Go way: a signed Git tag, a GitHub Release archive, and a Go proxy/pkg.go.dev indexing step from the self-hosted release workflow.

Documentation

Overview

Package qdecimal provides exact base-10 decimal arithmetic for finance, banking, exchanges, ledgers, and trading systems.

The core Decimal type stores values as coefficient * 10^-scale and preserves visible scale such as 0.00 or 123.4500. Public operations never mutate their receiver and never rely on package-global precision settings.

Operations that can lose information require an explicit scale and RoundingMode, or a Context/MoneyContext that carries that policy. Exact-only methods such as DivExact, AvgExact, RescaleExact, QuantizeExact, QuantizeStepExact, and context *Exact variants return ErrInexact instead of rounding.

The package also includes Fixed64 for bounded-scale int64 hot paths, exchange tick quantization, range checks, and aggregates, Money for currency-safe arithmetic, nullable wrappers for SQL/JSON boundaries, formatter support, versioned binary encodings, raw BSON helpers, fuzz tests, stress tests, and release gates designed for production finance use.

Index

Examples

Constants

View Source
const (
	DefaultMaxParseDigits         = 4096
	DefaultMaxParseScale          = 4096
	DefaultMaxParseExponentDigits = 10
)
View Source
const (
	RoundBankers  = ToNearestEven
	RoundHalfUp   = ToNearestAway
	RoundHalfDown = ToNearestTowardZero
	RoundUp       = AwayFromZero
	RoundDown     = TowardZero
	RoundCeil     = TowardPositive
	RoundFloor    = TowardNegative
)

Common finance-oriented aliases.

View Source
const DefaultMaxBSONDecimalTextBytes = DefaultMaxParseDigits + DefaultMaxParseExponentDigits + 16

DefaultMaxBSONDecimalTextBytes bounds untrusted BSON decimal text before it is copied into a Go string and parsed.

View Source
const (
	// DefaultMaxBinaryCoefficientBytes bounds untrusted binary decimal payloads.
	// Trusted storage can opt out with BinaryDecodeOptions.
	DefaultMaxBinaryCoefficientBytes = 4096
)
View Source
const DefaultMaxFormatScale int32 = DefaultMaxParseScale

DefaultMaxFormatScale caps precision-driven fmt rescaling for untrusted format strings. Call explicit Rescale/StringFixed APIs when a larger trusted scale is truly required.

Variables

View Source
var (
	Zero = NewFromInt(0)
	One  = NewFromInt(1)
	Ten  = NewFromInt(10)
)
View Source
var (
	// ErrInvalidSyntax indicates malformed decimal text.
	ErrInvalidSyntax = errors.New("qdecimal: invalid decimal syntax")
	// ErrInvalidScale indicates a negative or unsupported decimal scale.
	ErrInvalidScale = errors.New("qdecimal: invalid decimal scale")
	// ErrDivisionByZero indicates division by zero.
	ErrDivisionByZero = errors.New("qdecimal: division by zero")
	// ErrNonFiniteFloat indicates NaN or infinity was passed to a float constructor.
	ErrNonFiniteFloat = errors.New("qdecimal: non-finite float")
	// ErrNilValue indicates SQL NULL or JSON null was assigned to a non-nullable Decimal.
	ErrNilValue = errors.New("qdecimal: nil cannot be assigned to a non-null decimal")
	// ErrInvalidSource indicates an unsupported database scanner source type.
	ErrInvalidSource = errors.New("qdecimal: unsupported database source type")
	// ErrInvalidRoundingMode indicates an unknown rounding mode.
	ErrInvalidRoundingMode = errors.New("qdecimal: invalid rounding mode")
	// ErrOverflow indicates a requested conversion cannot fit in the target type.
	ErrOverflow = errors.New("qdecimal: overflow")
	// ErrInexact indicates an exact-only operation would require rounding.
	ErrInexact = errors.New("qdecimal: inexact decimal result")
	// ErrEmptyInput indicates an aggregate operation received no values.
	ErrEmptyInput = errors.New("qdecimal: empty input")
	// ErrInvalidCurrency indicates a malformed money currency code.
	ErrInvalidCurrency = errors.New("qdecimal: invalid currency code")
	// ErrCurrencyMismatch indicates money values with different currencies were combined.
	ErrCurrencyMismatch = errors.New("qdecimal: currency mismatch")
	// ErrInvalidAllocation indicates a money allocation with invalid parts or ratios.
	ErrInvalidAllocation = errors.New("qdecimal: invalid money allocation")
	// ErrLimitExceeded indicates input exceeded a configured parser resource limit.
	ErrLimitExceeded = errors.New("qdecimal: input exceeds configured limit")
)
View Source
var DefaultParseOptions = ParseOptions{
	AllowUnicodeMinus: true,
	AllowPlus:         true,
	DecimalSeparator:  '.',
	MaxDigits:         DefaultMaxParseDigits,
	MaxScale:          DefaultMaxParseScale,
	MaxExponentDigits: DefaultMaxParseExponentDigits,
}

DefaultParseOptions accepts canonical ASCII decimals plus the Unicode minus sign.

Functions

func MustMinorScale

func MustMinorScale(scale int32) int32

MustMinorScale validates common currency minor-unit scales for package initialization and tests.

func NormalizeCurrency

func NormalizeCurrency(currency string) (string, error)

NormalizeCurrency returns an uppercase currency/asset code.

Codes must be 3 to 12 ASCII letters or digits. This covers fiat codes such as USD and IDR, plus common exchange asset codes such as BTC, ETH, USDT, and USDC.

func ValidateMinorScale

func ValidateMinorScale(scale int32) error

ValidateMinorScale validates common currency minor-unit scales.

Types

type BinaryDecodeOptions

type BinaryDecodeOptions struct {
	MaxCoefficientBytes int
	MaxScale            int32
}

BinaryDecodeOptions controls resource limits while decoding versioned binary Decimal and Money payloads. A zero limit disables that specific limit.

func DefaultBinaryDecodeOptions

func DefaultBinaryDecodeOptions() BinaryDecodeOptions

DefaultBinaryDecodeOptions returns the limits used by UnmarshalBinary and GobDecode.

type Context

type Context struct {
	Scale    int32
	Rounding RoundingMode
}

Context is an explicit finance arithmetic policy.

It deliberately replaces package-global precision knobs: callers pass the policy they want at each boundary where rounding may occur.

Example
package main

import (
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	usdCents := qdecimal.MustContext(2, qdecimal.ToNearestEven)

	fee, err := usdCents.Mul(
		qdecimal.MustParse("123.4567"),
		qdecimal.MustParse("0.0025"),
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(fee)
}
Output:
0.31

func MustContext

func MustContext(scale int32, rounding RoundingMode) Context

MustContext is for package initialization and tests.

func NewContext

func NewContext(scale int32, rounding RoundingMode) (Context, error)

NewContext validates and returns a finance arithmetic context.

func (Context) Add

func (c Context) Add(a, b Decimal) (Decimal, error)

Add returns a + b rounded to the context scale.

func (Context) AddExact

func (c Context) AddExact(a, b Decimal) (Decimal, error)

AddExact returns a + b at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (Context) Avg

func (c Context) Avg(values ...Decimal) (Decimal, error)

Avg returns the average of values rounded to the context scale.

func (Context) AvgExact

func (c Context) AvgExact(values ...Decimal) (Decimal, error)

AvgExact returns the exact average at the context scale, failing with ErrInexact if the average repeats or does not fit the context scale.

func (Context) Div

func (c Context) Div(a, b Decimal) (Decimal, error)

Div returns a / b rounded to the context scale.

func (Context) DivExact

func (c Context) DivExact(a, b Decimal) (Decimal, error)

DivExact returns a / b at the context scale without rounding.

func (Context) MarshalJSON

func (c Context) MarshalJSON() ([]byte, error)

MarshalJSON emits a stable policy object for configuration and audit logs.

func (Context) Mul

func (c Context) Mul(a, b Decimal) (Decimal, error)

Mul returns a * b rounded to the context scale.

func (Context) MulExact

func (c Context) MulExact(a, b Decimal) (Decimal, error)

MulExact returns a * b at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (Context) Quantize

func (c Context) Quantize(d Decimal) (Decimal, error)

Quantize rounds d to the context scale.

func (Context) QuantizeExact

func (c Context) QuantizeExact(d Decimal) (Decimal, error)

QuantizeExact changes d to the context scale without discarding non-zero digits.

func (Context) QuantizeStep

func (c Context) QuantizeStep(d, step Decimal) (Decimal, error)

QuantizeStep rounds d to a valid increment, then to the context scale.

func (Context) QuantizeStepExact

func (c Context) QuantizeStepExact(d, step Decimal) (Decimal, error)

QuantizeStepExact changes d to the context scale only when d is already an exact multiple of step and no non-zero digits would be discarded.

func (Context) String

func (c Context) String() string

func (Context) StringFixed

func (c Context) StringFixed(d Decimal) (string, error)

StringFixed returns d rendered at the context scale.

func (Context) Sub

func (c Context) Sub(a, b Decimal) (Decimal, error)

Sub returns a - b rounded to the context scale.

func (Context) SubExact

func (c Context) SubExact(a, b Decimal) (Decimal, error)

SubExact returns a - b at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (Context) Sum

func (c Context) Sum(values ...Decimal) (Decimal, error)

Sum rounds the exact sum of values to the context scale.

func (Context) SumExact

func (c Context) SumExact(values ...Decimal) (Decimal, error)

SumExact returns the exact sum at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (*Context) UnmarshalJSON

func (c *Context) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes and validates a policy object.

type Decimal

type Decimal struct {
	// contains filtered or unexported fields
}

Decimal represents coef * 10^-scale.

Decimal has no NaN or infinity state. Non-finite values are rejected at input boundaries so finance code cannot silently propagate invalid amounts.

Example
package main

import (
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	price := qdecimal.MustParse("123.4500")
	size := qdecimal.MustParse("0.25")

	notional := price.Mul(size)
	rounded, err := notional.Round(2, qdecimal.ToNearestEven)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(rounded)
}
Output:
30.86

func Avg

func Avg(values []Decimal, scale int32, mode RoundingMode) (Decimal, error)

Avg returns the average of values rounded to scale using mode.

func AvgExact

func AvgExact(values []Decimal) (Decimal, error)

AvgExact returns the exact finite average of values. If the quotient repeats, ErrInexact is returned instead of rounding.

func FromBigFloat

func FromBigFloat(f *big.Float, scale int32, mode RoundingMode) (Decimal, error)

FromBigFloat rounds f to scale using mode.

func FromFloat64

func FromFloat64(v float64) (Decimal, error)

FromFloat64 converts a finite float through Go's shortest round-trip decimal representation. Prefer Parse or integer minor-unit constructors in finance code; this method is explicit because binary floats are not decimal inputs.

func FromRat

func FromRat(r *big.Rat, scale int32, mode RoundingMode) (Decimal, error)

FromRat rounds r to scale using mode.

func Max

func Max(values ...Decimal) Decimal

Max returns the larger value.

func Min

func Min(values ...Decimal) Decimal

Min returns the smaller value.

func MustParse

func MustParse(s string) Decimal

MustParse is for tests and package-level initialization. It panics only when explicitly requested by the caller.

func New

func New(coef int64, scale int32) (Decimal, error)

New creates a Decimal from an integer coefficient and non-negative scale.

func NewFromBigInt

func NewFromBigInt(coef *big.Int, scale int32) (Decimal, error)

NewFromBigInt creates a Decimal from a coefficient copy and non-negative scale.

func NewFromFloat

func NewFromFloat(v float64) (Decimal, error)

NewFromFloat is a compatibility alias for FromFloat64.

It returns ErrNonFiniteFloat for NaN and infinity instead of panicking.

func NewFromFloatWithScale

func NewFromFloatWithScale(v float64, scale int32, mode RoundingMode) (Decimal, error)

NewFromFloatWithScale converts a finite float and immediately rounds it to scale using mode.

This keeps float boundaries explicit: binary floats are accepted only at an integration edge, and any decimal rounding policy is supplied by the caller.

func NewFromInt

func NewFromInt(v int64) Decimal

NewFromInt creates an integer Decimal.

func NewFromMinorUnits

func NewFromMinorUnits(units int64, scale int32) (Decimal, error)

NewFromMinorUnits creates a Decimal from integer minor units.

Example: NewFromMinorUnits(12345, 2) represents 123.45.

func NewFromString

func NewFromString(s string) (Decimal, error)

NewFromString is a compatibility alias for Parse.

func NewFromUint64

func NewFromUint64(v uint64) Decimal

NewFromUint64 creates an integer Decimal from an unsigned value.

func Parse

func Parse(s string) (Decimal, error)

Parse parses a decimal string using DefaultParseOptions.

func ParseBytes

func ParseBytes(text []byte) (Decimal, error)

ParseBytes parses a decimal byte slice using DefaultParseOptions.

func ParseFlexible

func ParseFlexible(s string) (Decimal, error)

ParseFlexible parses common human-entry input: surrounding whitespace, Unicode minus, plus sign, and comma thousands separators.

func ParseWithOptions

func ParseWithOptions(s string, opts ParseOptions) (Decimal, error)

ParseWithOptions parses a decimal string with explicit syntax options.

func RequireFromString

func RequireFromString(s string) Decimal

RequireFromString is an alias for MustParse.

func Sum

func Sum(values ...Decimal) Decimal

Sum returns the exact sum of values. An empty input returns Zero.

func (Decimal) Abs

func (d Decimal) Abs() Decimal

Abs returns |d|.

func (Decimal) Add

func (d Decimal) Add(other Decimal) Decimal

Add returns d + other exactly.

func (Decimal) AppendBinary

func (d Decimal) AppendBinary(dst []byte) ([]byte, error)

AppendBinary appends d's stable binary representation to dst.

func (Decimal) AppendText

func (d Decimal) AppendText(dst []byte) ([]byte, error)

AppendText appends d's text representation to dst.

func (Decimal) Between

func (d Decimal) Between(min, max Decimal, inclusive bool) bool

Between reports whether d is inside [min, max] when inclusive is true, or inside (min, max) when inclusive is false. Reversed bounds are accepted.

func (Decimal) BinarySize

func (d Decimal) BinarySize() int

BinarySize returns the exact number of bytes produced by MarshalBinary.

func (Decimal) Ceil

func (d Decimal) Ceil(scale int32) (Decimal, error)

Ceil rounds toward +infinity to scale.

func (Decimal) Clamp

func (d Decimal) Clamp(min, max Decimal) Decimal

Clamp constrains d to [min, max]. Reversed bounds are accepted.

func (Decimal) Cmp

func (d Decimal) Cmp(other Decimal) int

Cmp compares d and other numerically.

func (Decimal) Coefficient

func (d Decimal) Coefficient() *big.Int

Coefficient returns a defensive copy of d's unscaled integer coefficient.

func (Decimal) Div

func (d Decimal) Div(other Decimal, scale int32, mode RoundingMode) (Decimal, error)

Div divides d by other and rounds the result to scale using mode.

func (Decimal) DivExact

func (d Decimal) DivExact(other Decimal) (Decimal, error)

DivExact divides d by other and returns an exact finite decimal. If the quotient has a repeating decimal expansion, ErrInexact is returned instead of rounding.

func (Decimal) Equal

func (d Decimal) Equal(other Decimal) bool

Equal reports numeric equality.

func (Decimal) Floor

func (d Decimal) Floor(scale int32) (Decimal, error)

Floor rounds toward -infinity to scale.

func (Decimal) Format

func (d Decimal) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (*Decimal) GobDecode

func (d *Decimal) GobDecode(data []byte) error

GobDecode implements gob.GobDecoder using the stable binary format.

func (Decimal) GobEncode

func (d Decimal) GobEncode() ([]byte, error)

GobEncode implements gob.GobEncoder using the stable binary format.

func (Decimal) Int64MinorUnits

func (d Decimal) Int64MinorUnits(scale int32, mode RoundingMode) (int64, error)

Int64MinorUnits is like MinorUnits but fails if the result does not fit int64.

func (Decimal) Int64MinorUnitsExact

func (d Decimal) Int64MinorUnitsExact(scale int32) (int64, error)

Int64MinorUnitsExact is like MinorUnitsExact but fails if the result does not fit int64.

func (Decimal) IsInteger

func (d Decimal) IsInteger() bool

IsInteger reports whether d has no non-zero fractional digits.

func (Decimal) IsZero

func (d Decimal) IsZero() bool

IsZero reports whether d is numerically zero.

func (Decimal) JSONNumber

func (d Decimal) JSONNumber() json.Number

JSONNumber returns a json.Number for systems that explicitly require numeric JSON tokens and can preserve arbitrary precision.

func (Decimal) Key

func (d Decimal) Key() string

Key returns a canonical comparable representation suitable for map keys.

func (Decimal) MarshalBSONDocument

func (d Decimal) MarshalBSONDocument(field string) ([]byte, error)

MarshalBSONDocument returns a minimal BSON document with one string field.

This is a driver-neutral boundary for document databases and message stores that accept raw BSON. It intentionally stores the decimal as precision-safe text rather than lossy floating-point data.

func (Decimal) MarshalBSONStringValue

func (d Decimal) MarshalBSONStringValue() ([]byte, error)

MarshalBSONStringValue returns the raw BSON string value bytes for d.

The bytes are the BSON value payload only:

int32 byte-length including NUL | UTF-8 decimal text | NUL

qdecimal uses string values for this dependency-free BSON bridge so arbitrary precision and preserved scale are not limited by Decimal128's finite range.

func (Decimal) MarshalBinary

func (d Decimal) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler using a stable versioned network-order format:

QDEC | version | scale uint32 | coefficient length uint32 | coefficient bytes

The coefficient is stored as signed magnitude: one sign byte plus big-endian absolute coefficient bytes.

func (Decimal) MarshalExtendedJSON

func (d Decimal) MarshalExtendedJSON() ([]byte, error)

MarshalExtendedJSON emits MongoDB-style Decimal128 Extended JSON.

func (Decimal) MarshalJSON

func (d Decimal) MarshalJSON() ([]byte, error)

MarshalJSON emits a JSON string. This avoids lossy float interpretation in JavaScript, database gateways, and message buses.

Example
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	data, err := json.Marshal(qdecimal.MustParse("123.4500"))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}
Output:
"123.4500"

func (Decimal) MarshalJSONWithMode

func (d Decimal) MarshalJSONWithMode(mode JSONMode) ([]byte, error)

MarshalJSONWithMode emits d using an explicit JSON policy.

func (Decimal) MarshalText

func (d Decimal) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Decimal) MinorUnits

func (d Decimal) MinorUnits(scale int32, mode RoundingMode) (*big.Int, error)

MinorUnits returns d rounded to scale and represented as an integer number of minor units.

func (Decimal) MinorUnitsExact

func (d Decimal) MinorUnitsExact(scale int32) (*big.Int, error)

MinorUnitsExact returns d as minor units only when no non-zero digit would be discarded at scale.

func (Decimal) Mul

func (d Decimal) Mul(other Decimal) Decimal

Mul returns d * other exactly.

func (Decimal) Neg

func (d Decimal) Neg() Decimal

Neg returns -d.

func (Decimal) Normalize

func (d Decimal) Normalize() Decimal

Normalize removes insignificant trailing fractional zeros.

func (Decimal) Pow

func (d Decimal) Pow(exp Decimal, scale int32, mode RoundingMode) (Decimal, error)

Pow returns d^exp rounded to scale using mode.

exp must be an integer-valued Decimal. Fractional exponents are rejected with ErrInexact instead of using a hidden floating-point approximation. Use PowInt when a non-negative integer exponent should preserve the exact natural scale.

func (Decimal) PowInt

func (d Decimal) PowInt(exp uint64) (Decimal, error)

PowInt returns d^exp exactly for non-negative integer exponents.

func (Decimal) Quantize

func (d Decimal) Quantize(template Decimal, mode RoundingMode) (Decimal, error)

Quantize rounds d to the same scale as template.

func (Decimal) QuantizeExact

func (d Decimal) QuantizeExact(template Decimal) (Decimal, error)

QuantizeExact changes d to template's scale only when no non-zero digit would be lost.

func (Decimal) QuantizeStep

func (d Decimal) QuantizeStep(step Decimal, mode RoundingMode) (Decimal, error)

QuantizeStep rounds d to the nearest multiple of step using mode.

This is intended for exchange tick sizes and banking increments that are not expressible by scale alone, such as 0.05.

func (Decimal) QuantizeStepExact

func (d Decimal) QuantizeStepExact(step Decimal) (Decimal, error)

QuantizeStepExact changes d to step's scale only when d is already an exact multiple of step. ErrInexact is returned instead of rounding.

func (Decimal) Rat

func (d Decimal) Rat() *big.Rat

Rat returns an exact rational copy of d.

func (Decimal) Rescale

func (d Decimal) Rescale(scale int32, mode RoundingMode) (Decimal, error)

Rescale changes d to scale using mode when digits must be discarded.

func (Decimal) RescaleExact

func (d Decimal) RescaleExact(scale int32) (Decimal, error)

RescaleExact changes d to scale only when no non-zero digit would be lost.

If reducing scale would require rounding, ErrInexact is returned.

func (Decimal) Round

func (d Decimal) Round(scale int32, mode RoundingMode) (Decimal, error)

Round is an alias for Rescale.

func (Decimal) Scale

func (d Decimal) Scale() int32

Scale returns the number of fractional decimal digits preserved by d.

func (*Decimal) Scan

func (d *Decimal) Scan(src any) error

Scan implements database/sql.Scanner.

func (Decimal) Sign

func (d Decimal) Sign() int

Sign returns -1, 0, or +1.

func (Decimal) String

func (d Decimal) String() string

String returns the decimal string while preserving scale, including values like 0.00.

func (Decimal) StringFixed

func (d Decimal) StringFixed(scale int32, mode RoundingMode) (string, error)

StringFixed rounds d to scale and returns the fixed-scale representation.

func (Decimal) Sub

func (d Decimal) Sub(other Decimal) Decimal

Sub returns d - other exactly.

func (Decimal) Truncate

func (d Decimal) Truncate(scale int32) (Decimal, error)

Truncate rounds toward zero to scale.

func (*Decimal) UnmarshalBSONDocument

func (d *Decimal) UnmarshalBSONDocument(data []byte, field string) error

UnmarshalBSONDocument decodes a minimal single-field BSON document into d.

func (*Decimal) UnmarshalBSONStringValue

func (d *Decimal) UnmarshalBSONStringValue(data []byte) error

UnmarshalBSONStringValue decodes a raw BSON string value payload into d.

func (*Decimal) UnmarshalBinary

func (d *Decimal) UnmarshalBinary(data []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler using DefaultBinaryDecodeOptions().

func (*Decimal) UnmarshalBinaryWithOptions

func (d *Decimal) UnmarshalBinaryWithOptions(data []byte, opts BinaryDecodeOptions) error

UnmarshalBinaryWithOptions decodes d's stable binary representation with explicit resource limits for trusted or untrusted storage boundaries.

func (*Decimal) UnmarshalJSON

func (d *Decimal) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts either a JSON string or a JSON number.

func (*Decimal) UnmarshalText

func (d *Decimal) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (Decimal) Value

func (d Decimal) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type ExtendedJSON

type ExtendedJSON struct {
	Decimal Decimal
}

ExtendedJSON wraps Decimal using MongoDB-style Decimal128 Extended JSON:

{"$numberDecimal":"123.45"}

It intentionally avoids importing a database driver. Driver-specific BSON adapters can build on this stable representation.

func AsExtendedJSON

func AsExtendedJSON(d Decimal) ExtendedJSON

AsExtendedJSON returns a MongoDB-style Extended JSON wrapper for d.

func (ExtendedJSON) IsZero

func (e ExtendedJSON) IsZero() bool

IsZero reports whether the wrapped decimal is numerically zero.

func (ExtendedJSON) MarshalJSON

func (e ExtendedJSON) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*ExtendedJSON) UnmarshalJSON

func (e *ExtendedJSON) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

type Fixed64

type Fixed64 struct {
	// contains filtered or unexported fields
}

Fixed64 is a compact fixed-scale decimal for hot ledger and trading paths.

It stores integer minor units and a non-negative scale. Use Decimal for arbitrary precision; use Fixed64 when the business domain has a known bounded scale and int64 range is sufficient.

Example
package main

import (
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	price, err := qdecimal.ParseFixed64("123.456", 2, qdecimal.ToNearestAway)
	if err != nil {
		log.Fatal(err)
	}
	rate, err := qdecimal.NewFixed64(25, 4)
	if err != nil {
		log.Fatal(err)
	}
	fee, err := price.Mul(rate, 4, qdecimal.ToNearestEven)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(fee)
}
Output:
0.3086

func AvgFixed64

func AvgFixed64(values []Fixed64, scale int32, mode RoundingMode) (Fixed64, error)

AvgFixed64 returns the average of values rounded to scale using mode.

func AvgFixed64Exact

func AvgFixed64Exact(values []Fixed64, scale int32) (Fixed64, error)

AvgFixed64Exact returns the exact finite average of values at scale. If the quotient repeats or does not fit scale, ErrInexact is returned.

func Fixed64FromDecimal

func Fixed64FromDecimal(d Decimal, scale int32, mode RoundingMode) (Fixed64, error)

Fixed64FromDecimal converts d to Fixed64 at scale using mode.

func MaxFixed64

func MaxFixed64(values ...Fixed64) Fixed64

MaxFixed64 returns the largest value. An empty input returns the zero value.

func MinFixed64

func MinFixed64(values ...Fixed64) Fixed64

MinFixed64 returns the smallest value. An empty input returns the zero value.

func NewFixed64

func NewFixed64(units int64, scale int32) (Fixed64, error)

NewFixed64 creates a fixed-scale decimal from integer units.

func ParseFixed64

func ParseFixed64(s string, scale int32, mode RoundingMode) (Fixed64, error)

ParseFixed64 parses input and rounds it to scale.

func SumFixed64

func SumFixed64(values ...Fixed64) (Fixed64, error)

SumFixed64 returns the exact sum of values. The fast path keeps same-scale sums in int64 units; mixed-scale or overflowing sums fall back to Decimal and still return ErrOverflow if the final exact result cannot fit in Fixed64.

func (Fixed64) Abs

func (f Fixed64) Abs() (Fixed64, error)

Abs returns |f|, checking int64 overflow.

func (Fixed64) Add

func (f Fixed64) Add(other Fixed64) (Fixed64, error)

Add returns f + other exactly, aligning scales when possible.

func (Fixed64) AppendBinary

func (f Fixed64) AppendBinary(dst []byte) ([]byte, error)

AppendBinary appends f's stable binary representation to dst.

func (Fixed64) AppendText

func (f Fixed64) AppendText(dst []byte) ([]byte, error)

AppendText appends f's text representation to dst.

func (Fixed64) Between

func (f Fixed64) Between(min, max Fixed64, inclusive bool) bool

Between reports whether f is inside [min, max] when inclusive is true, or inside (min, max) when inclusive is false. Reversed bounds are accepted.

func (Fixed64) BinarySize

func (f Fixed64) BinarySize() int

BinarySize returns the exact number of bytes produced by MarshalBinary.

func (Fixed64) Ceil

func (f Fixed64) Ceil(scale int32) (Fixed64, error)

Ceil rounds toward +infinity to scale.

func (Fixed64) Clamp

func (f Fixed64) Clamp(min, max Fixed64) Fixed64

Clamp constrains f to [min, max]. Reversed bounds are accepted.

func (Fixed64) Cmp

func (f Fixed64) Cmp(other Fixed64) int

Cmp compares f and other numerically.

func (Fixed64) Decimal

func (f Fixed64) Decimal() Decimal

Decimal converts f to arbitrary-precision Decimal exactly.

func (Fixed64) Div

func (f Fixed64) Div(other Fixed64, scale int32, mode RoundingMode) (Fixed64, error)

Div returns f / other rounded to scale using mode.

func (Fixed64) Equal

func (f Fixed64) Equal(other Fixed64) bool

Equal reports numeric equality.

func (Fixed64) Floor

func (f Fixed64) Floor(scale int32) (Fixed64, error)

Floor rounds toward -infinity to scale.

func (Fixed64) Format

func (f Fixed64) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (*Fixed64) GobDecode

func (f *Fixed64) GobDecode(data []byte) error

GobDecode implements gob.GobDecoder using the stable binary format.

func (Fixed64) GobEncode

func (f Fixed64) GobEncode() ([]byte, error)

GobEncode implements gob.GobEncoder using the stable binary format.

func (Fixed64) IsZero

func (f Fixed64) IsZero() bool

IsZero reports whether f is zero.

func (Fixed64) Key

func (f Fixed64) Key() string

Key returns a canonical comparable representation suitable for map keys.

func (Fixed64) MarshalBinary

func (f Fixed64) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler using a stable versioned network-order fixed64 format:

QF64 | version | scale uint32 | units int64

func (Fixed64) MarshalJSON

func (f Fixed64) MarshalJSON() ([]byte, error)

MarshalJSON emits a precision-preserving JSON string.

func (Fixed64) MarshalText

func (f Fixed64) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Fixed64) Mul

func (f Fixed64) Mul(other Fixed64, scale int32, mode RoundingMode) (Fixed64, error)

Mul returns f * other rounded to scale using mode.

func (Fixed64) Neg

func (f Fixed64) Neg() (Fixed64, error)

Neg returns -f, checking int64 overflow.

func (Fixed64) QuantizeStep

func (f Fixed64) QuantizeStep(step Fixed64, mode RoundingMode) (Fixed64, error)

QuantizeStep rounds f to a valid multiple of step using mode.

This is intended for bounded-scale exchange ticks, lot sizes, and banking increments. The returned value uses step's scale.

func (Fixed64) Rescale

func (f Fixed64) Rescale(scale int32, mode RoundingMode) (Fixed64, error)

Rescale changes f to scale using mode when minor digits must be discarded.

func (Fixed64) Round

func (f Fixed64) Round(scale int32, mode RoundingMode) (Fixed64, error)

Round is an alias for Rescale.

func (Fixed64) Scale

func (f Fixed64) Scale() int32

Scale returns the fixed decimal scale.

func (*Fixed64) Scan

func (f *Fixed64) Scan(src any) error

Scan implements database/sql.Scanner.

func (Fixed64) Sign

func (f Fixed64) Sign() int

Sign returns -1, 0, or +1.

func (Fixed64) String

func (f Fixed64) String() string

String returns f's fixed-scale decimal representation.

func (Fixed64) Sub

func (f Fixed64) Sub(other Fixed64) (Fixed64, error)

Sub returns f - other exactly, aligning scales when possible.

func (Fixed64) Truncate

func (f Fixed64) Truncate(scale int32) (Fixed64, error)

Truncate rounds toward zero to scale.

func (Fixed64) Units

func (f Fixed64) Units() int64

Units returns the integer minor units.

func (*Fixed64) UnmarshalBinary

func (f *Fixed64) UnmarshalBinary(data []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler.

func (*Fixed64) UnmarshalJSON

func (f *Fixed64) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts a JSON string or number.

func (*Fixed64) UnmarshalText

func (f *Fixed64) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler and preserves the parsed scale.

func (Fixed64) Value

func (f Fixed64) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type JSONMode

type JSONMode byte

JSONMode selects how decimals are emitted as JSON.

const (
	// EmitJSONString emits a quoted decimal string. This is the safest default.
	EmitJSONString JSONMode = iota
	// EmitJSONNumber emits an unquoted JSON number token.
	EmitJSONNumber
)

type Money

type Money struct {
	// contains filtered or unexported fields
}

Money couples an exact Decimal amount with a normalized currency or asset code.

Money prevents accidental arithmetic across currencies. It does not embed an ISO-4217 table; callers choose scale and rounding policies explicitly so the library does not ship stale monetary metadata.

func AvgMoney

func AvgMoney(values []Money, scale int32, mode RoundingMode) (Money, error)

AvgMoney returns the average of money values rounded to scale using mode. All values must use one currency.

func AvgMoneyExact

func AvgMoneyExact(values []Money) (Money, error)

AvgMoneyExact returns the exact finite average of money values. If the quotient repeats, ErrInexact is returned instead of rounding.

func MaxMoney

func MaxMoney(values ...Money) (Money, error)

MaxMoney returns the largest money value. All values must use one currency.

func MinMoney

func MinMoney(values ...Money) (Money, error)

MinMoney returns the smallest money value. All values must use one currency.

func MustParseMoney

func MustParseMoney(text string) Money

MustParseMoney is for tests and package-level initialization. It panics only when explicitly requested by the caller.

func NewMoney

func NewMoney(amount Decimal, currency string) (Money, error)

NewMoney creates a Money value and normalizes the currency code.

func NewMoneyFromMinorUnits

func NewMoneyFromMinorUnits(units int64, scale int32, currency string) (Money, error)

NewMoneyFromMinorUnits creates Money from integer minor units and an explicit scale.

func ParseMoney

func ParseMoney(text string) (Money, error)

ParseMoney parses canonical "CODE amount" text.

Example: ParseMoney("USD 123.45").

func SumMoney

func SumMoney(values ...Money) (Money, error)

SumMoney returns the exact sum of money values. All values must use one currency because summing across currencies is a category error.

func (Money) Abs

func (m Money) Abs() Money

Abs returns |m|.

func (Money) Add

func (m Money) Add(other Money) (Money, error)

Add returns m + other. It fails if currencies differ.

func (Money) Allocate

func (m Money) Allocate(parts int, scale int32, mode RoundingMode) ([]Money, error)

Allocate splits m into parts at scale while preserving the rounded total.

Remainder minor units are distributed from the first part forward. Negative values distribute negative remainders the same way, preserving exact totals.

func (Money) AllocateRatios

func (m Money) AllocateRatios(ratios []int64, scale int32, mode RoundingMode) ([]Money, error)

AllocateRatios splits m according to non-negative ratios at scale while preserving the rounded total.

func (Money) Amount

func (m Money) Amount() Decimal

Amount returns a defensive copy of the decimal amount.

func (Money) AppendBinary

func (m Money) AppendBinary(dst []byte) ([]byte, error)

AppendBinary appends m's stable binary representation to dst.

func (Money) AppendText

func (m Money) AppendText(dst []byte) ([]byte, error)

AppendText appends m's canonical "CODE amount" text representation to dst.

func (Money) Between

func (m Money) Between(min, max Money, inclusive bool) (bool, error)

Between reports whether m is inside [min, max] when inclusive is true, or inside (min, max) when inclusive is false. Reversed bounds are accepted.

func (Money) BinarySize

func (m Money) BinarySize() int

BinarySize returns the exact number of bytes produced by MarshalBinary.

func (Money) Clamp

func (m Money) Clamp(min, max Money) (Money, error)

Clamp constrains m to [min, max]. Reversed bounds are accepted.

func (Money) Cmp

func (m Money) Cmp(other Money) (int, error)

Cmp compares two money values with the same currency.

func (Money) Currency

func (m Money) Currency() string

Currency returns the normalized currency or asset code.

func (Money) Div

func (m Money) Div(divisor Decimal, scale int32, mode RoundingMode) (Money, error)

Div divides m by divisor and rounds the result to scale using mode.

func (Money) Equal

func (m Money) Equal(other Money) bool

Equal reports whether amount and currency are equal.

func (Money) Format

func (m Money) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (*Money) GobDecode

func (m *Money) GobDecode(data []byte) error

GobDecode implements gob.GobDecoder using the stable binary format.

func (Money) GobEncode

func (m Money) GobEncode() ([]byte, error)

GobEncode implements gob.GobEncoder using the stable binary format.

func (Money) Int64MinorUnits

func (m Money) Int64MinorUnits(scale int32, mode RoundingMode) (int64, error)

Int64MinorUnits is like MinorUnits but fails if the result does not fit int64.

func (Money) Int64MinorUnitsExact

func (m Money) Int64MinorUnitsExact(scale int32) (int64, error)

Int64MinorUnitsExact is like MinorUnitsExact but fails if the result does not fit int64.

func (Money) IsZero

func (m Money) IsZero() bool

IsZero reports whether the amount is zero.

func (Money) Key

func (m Money) Key() string

Key returns a canonical comparable representation suitable for map keys.

func (Money) MarshalBinary

func (m Money) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler using a stable versioned network-order money format:

QMON | version | currency length uint16 | currency bytes |
decimal length uint32 | decimal binary bytes

func (Money) MarshalJSON

func (m Money) MarshalJSON() ([]byte, error)

MarshalJSON emits {"amount":"...","currency":"..."}.

func (Money) MarshalText

func (m Money) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Money) MinorUnits

func (m Money) MinorUnits(scale int32, mode RoundingMode) (*big.Int, error)

MinorUnits returns m rounded to scale as integer minor units.

func (Money) MinorUnitsExact

func (m Money) MinorUnitsExact(scale int32) (*big.Int, error)

MinorUnitsExact returns m as integer minor units only when no non-zero digit would be discarded at scale.

func (Money) Mul

func (m Money) Mul(factor Decimal, scale int32, mode RoundingMode) (Money, error)

Mul multiplies m by factor and rounds the result to scale using mode.

func (Money) Neg

func (m Money) Neg() Money

Neg returns -m.

func (Money) QuantizeStep

func (m Money) QuantizeStep(step Decimal, mode RoundingMode) (Money, error)

QuantizeStep rounds m's amount to a valid increment, such as an exchange tick.

func (Money) QuantizeStepExact

func (m Money) QuantizeStepExact(step Decimal) (Money, error)

QuantizeStepExact changes m's amount to step's scale only when it is already an exact multiple of step.

func (Money) Round

func (m Money) Round(scale int32, mode RoundingMode) (Money, error)

Round rounds m's amount to scale using mode.

func (Money) RoundExact

func (m Money) RoundExact(scale int32) (Money, error)

RoundExact changes m's amount to scale only when no non-zero digit would be lost.

func (*Money) Scan

func (m *Money) Scan(src any) error

Scan implements database/sql.Scanner using the canonical "CODE amount" text format.

func (Money) Sign

func (m Money) Sign() int

Sign returns -1, 0, or +1 for the amount.

func (Money) String

func (m Money) String() string

String returns a human-readable money representation.

func (Money) Sub

func (m Money) Sub(other Money) (Money, error)

Sub returns m - other. It fails if currencies differ.

func (*Money) UnmarshalBinary

func (m *Money) UnmarshalBinary(data []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler using DefaultBinaryDecodeOptions() for the embedded Decimal payload.

func (*Money) UnmarshalBinaryWithOptions

func (m *Money) UnmarshalBinaryWithOptions(data []byte, opts BinaryDecodeOptions) error

UnmarshalBinaryWithOptions decodes a Money binary payload with explicit resource limits for the embedded Decimal amount.

func (*Money) UnmarshalJSON

func (m *Money) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts {"amount":"...","currency":"..."}.

func (*Money) UnmarshalText

func (m *Money) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler for "CODE amount" text.

func (Money) Value

func (m Money) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer using the canonical "CODE amount" text format.

type MoneyContext

type MoneyContext struct {
	Currency string
	Scale    int32
	Rounding RoundingMode
}

MoneyContext is an explicit policy for one currency or asset.

It carries currency, scale, and rounding together so services can pass an auditable money policy without package-global precision or currency metadata.

Example
package main

import (
	"fmt"
	"log"

	"github.com/MeViksry/qdecimal"
)

func main() {
	usd := qdecimal.MustMoneyContext("usd", 2, qdecimal.ToNearestAway)

	amount, err := usd.Parse("10.005")
	if err != nil {
		log.Fatal(err)
	}
	rebate, err := usd.Parse("0.005")
	if err != nil {
		log.Fatal(err)
	}
	total, err := usd.Add(amount, rebate)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(total)
}
Output:
USD 10.02

func MustMoneyContext

func MustMoneyContext(currency string, scale int32, rounding RoundingMode) MoneyContext

MustMoneyContext is for package initialization and tests.

func NewMoneyContext

func NewMoneyContext(currency string, scale int32, rounding RoundingMode) (MoneyContext, error)

NewMoneyContext validates and returns a money arithmetic context.

func (MoneyContext) Add

func (c MoneyContext) Add(a, b Money) (Money, error)

Add returns a + b rounded to the context scale.

func (MoneyContext) AddExact

func (c MoneyContext) AddExact(a, b Money) (Money, error)

AddExact returns a + b at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (MoneyContext) Allocate

func (c MoneyContext) Allocate(m Money, parts int) ([]Money, error)

Allocate splits money into equal parts at the context scale.

func (MoneyContext) AllocateRatios

func (c MoneyContext) AllocateRatios(m Money, ratios []int64) ([]Money, error)

AllocateRatios splits money by ratios at the context scale.

func (MoneyContext) Avg

func (c MoneyContext) Avg(values ...Money) (Money, error)

Avg returns the average of values rounded to the context scale.

func (MoneyContext) AvgExact

func (c MoneyContext) AvgExact(values ...Money) (Money, error)

AvgExact returns the average at the context scale without rounding.

func (MoneyContext) Between

func (c MoneyContext) Between(m, min, max Money, inclusive bool) (bool, error)

Between reports whether m is inside the range after all values are quantized to the context scale. Reversed bounds are accepted.

func (MoneyContext) Clamp

func (c MoneyContext) Clamp(m, min, max Money) (Money, error)

Clamp constrains m to [min, max] after quantizing all values to the context scale. Reversed bounds are accepted.

func (MoneyContext) DecimalContext

func (c MoneyContext) DecimalContext() Context

DecimalContext returns the numeric scale/rounding policy.

func (MoneyContext) Div

func (c MoneyContext) Div(m Money, divisor Decimal) (Money, error)

Div divides money by divisor and rounds to the context scale.

func (MoneyContext) DivExact

func (c MoneyContext) DivExact(m Money, divisor Decimal) (Money, error)

DivExact divides money by divisor at the context scale without rounding.

func (MoneyContext) FromMinorUnits

func (c MoneyContext) FromMinorUnits(units int64) (Money, error)

FromMinorUnits creates money from integer minor units at the context scale.

func (MoneyContext) Int64MinorUnits

func (c MoneyContext) Int64MinorUnits(m Money) (int64, error)

Int64MinorUnits returns money as int64 minor units at the context scale.

func (MoneyContext) Int64MinorUnitsExact

func (c MoneyContext) Int64MinorUnitsExact(m Money) (int64, error)

Int64MinorUnitsExact returns money as int64 minor units at the context scale only when no non-zero digit would be discarded.

func (MoneyContext) MarshalJSON

func (c MoneyContext) MarshalJSON() ([]byte, error)

MarshalJSON emits a stable policy object for configuration and audit logs.

func (MoneyContext) Max

func (c MoneyContext) Max(values ...Money) (Money, error)

Max returns the largest value after quantizing all inputs to the context scale.

func (MoneyContext) Min

func (c MoneyContext) Min(values ...Money) (Money, error)

Min returns the smallest value after quantizing all inputs to the context scale.

func (MoneyContext) Money

func (c MoneyContext) Money(amount Decimal) (Money, error)

Money rounds amount to the context scale and attaches the context currency.

func (MoneyContext) MoneyExact

func (c MoneyContext) MoneyExact(amount Decimal) (Money, error)

MoneyExact attaches the context currency only when amount already fits the context scale without losing non-zero digits.

func (MoneyContext) Mul

func (c MoneyContext) Mul(m Money, factor Decimal) (Money, error)

Mul multiplies money by factor and rounds to the context scale.

func (MoneyContext) MulExact

func (c MoneyContext) MulExact(m Money, factor Decimal) (Money, error)

MulExact multiplies money by factor at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (MoneyContext) Parse

func (c MoneyContext) Parse(text string) (Money, error)

Parse parses amount text, rounds it to the context scale, and attaches currency.

func (MoneyContext) ParseFlexible

func (c MoneyContext) ParseFlexible(text string) (Money, error)

ParseFlexible parses human-entry text using ParseFlexible, rounds it, and attaches currency.

func (MoneyContext) Quantize

func (c MoneyContext) Quantize(m Money) (Money, error)

Quantize rounds money to the context scale after validating currency.

func (MoneyContext) QuantizeExact

func (c MoneyContext) QuantizeExact(m Money) (Money, error)

QuantizeExact changes money to the context scale without discarding non-zero digits.

func (MoneyContext) QuantizeStep

func (c MoneyContext) QuantizeStep(m Money, step Decimal) (Money, error)

QuantizeStep rounds money to a valid increment, then to the context scale.

func (MoneyContext) QuantizeStepExact

func (c MoneyContext) QuantizeStepExact(m Money, step Decimal) (Money, error)

QuantizeStepExact changes money to the context scale only when it is already an exact multiple of step and no non-zero digits would be discarded.

func (MoneyContext) String

func (c MoneyContext) String() string

func (MoneyContext) Sub

func (c MoneyContext) Sub(a, b Money) (Money, error)

Sub returns a - b rounded to the context scale.

func (MoneyContext) SubExact

func (c MoneyContext) SubExact(a, b Money) (Money, error)

SubExact returns a - b at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (MoneyContext) Sum

func (c MoneyContext) Sum(values ...Money) (Money, error)

Sum returns the exact sum of values rounded to the context scale.

func (MoneyContext) SumExact

func (c MoneyContext) SumExact(values ...Money) (Money, error)

SumExact returns the exact sum of values at the context scale, failing with ErrInexact if non-zero digits would be discarded.

func (*MoneyContext) UnmarshalJSON

func (c *MoneyContext) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes, normalizes, and validates a money policy object.

func (MoneyContext) WithRounding

func (c MoneyContext) WithRounding(rounding RoundingMode) (MoneyContext, error)

WithRounding returns c with a different rounding mode.

func (MoneyContext) WithScale

func (c MoneyContext) WithScale(scale int32) (MoneyContext, error)

WithScale returns c with a different scale.

type NullDecimal

type NullDecimal struct {
	Decimal Decimal
	Valid   bool
}

NullDecimal represents a Decimal that may be SQL NULL or JSON null.

func NewNullDecimal

func NewNullDecimal(d Decimal) NullDecimal

NewNullDecimal marks d as valid even when d is zero.

func (NullDecimal) AppendText

func (n NullDecimal) AppendText(dst []byte) ([]byte, error)

AppendText appends n's text representation to dst.

func (NullDecimal) Format

func (n NullDecimal) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (NullDecimal) IsZero

func (n NullDecimal) IsZero() bool

IsZero reports whether n is invalid/null.

func (NullDecimal) MarshalJSON

func (n NullDecimal) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (NullDecimal) MarshalText

func (n NullDecimal) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*NullDecimal) Scan

func (n *NullDecimal) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullDecimal) String

func (n NullDecimal) String() string

String returns n's decimal text or "null".

func (*NullDecimal) UnmarshalJSON

func (n *NullDecimal) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (*NullDecimal) UnmarshalText

func (n *NullDecimal) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (NullDecimal) Value

func (n NullDecimal) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type NullFixed64

type NullFixed64 struct {
	Fixed64 Fixed64
	Valid   bool
}

NullFixed64 represents a Fixed64 that may be SQL NULL or JSON null.

func NewNullFixed64

func NewNullFixed64(f Fixed64) NullFixed64

NewNullFixed64 marks f as valid even when f is zero.

func (NullFixed64) AppendText

func (n NullFixed64) AppendText(dst []byte) ([]byte, error)

AppendText appends n's text representation to dst.

func (NullFixed64) Format

func (n NullFixed64) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (NullFixed64) IsZero

func (n NullFixed64) IsZero() bool

IsZero reports whether n is invalid/null.

func (NullFixed64) MarshalJSON

func (n NullFixed64) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (NullFixed64) MarshalText

func (n NullFixed64) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*NullFixed64) Scan

func (n *NullFixed64) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullFixed64) String

func (n NullFixed64) String() string

String returns n's fixed decimal text or "null".

func (*NullFixed64) UnmarshalJSON

func (n *NullFixed64) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (*NullFixed64) UnmarshalText

func (n *NullFixed64) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (NullFixed64) Value

func (n NullFixed64) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type NullMoney

type NullMoney struct {
	Money Money
	Valid bool
}

NullMoney represents Money that may be SQL NULL or JSON null.

func NewNullMoney

func NewNullMoney(m Money) NullMoney

NewNullMoney marks m as valid.

func (NullMoney) AppendText

func (n NullMoney) AppendText(dst []byte) ([]byte, error)

AppendText appends n's text representation to dst.

func (NullMoney) Format

func (n NullMoney) Format(s fmt.State, verb rune)

Format implements fmt.Formatter.

func (NullMoney) IsZero

func (n NullMoney) IsZero() bool

IsZero reports whether n is invalid/null.

func (NullMoney) MarshalJSON

func (n NullMoney) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (NullMoney) MarshalText

func (n NullMoney) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*NullMoney) Scan

func (n *NullMoney) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullMoney) String

func (n NullMoney) String() string

String returns n's money text or "null".

func (*NullMoney) UnmarshalJSON

func (n *NullMoney) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (*NullMoney) UnmarshalText

func (n *NullMoney) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (NullMoney) Value

func (n NullMoney) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type Number

type Number struct {
	Decimal Decimal
}

Number wraps Decimal to marshal as a JSON number token instead of the default quoted string. Use it only with systems that preserve arbitrary-precision JSON numbers end to end.

func AsNumber

func AsNumber(d Decimal) Number

AsNumber returns a JSON-number wrapper for d.

func (Number) IsZero

func (n Number) IsZero() bool

IsZero reports whether the wrapped decimal is numerically zero.

func (Number) MarshalJSON

func (n Number) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*Number) UnmarshalJSON

func (n *Number) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

type ParseOptions

type ParseOptions struct {
	TrimSpace          bool
	AllowUnicodeMinus  bool
	AllowPlus          bool
	AllowThousands     bool
	DecimalSeparator   rune
	ThousandsSeparator rune
	MaxDigits          int
	MaxScale           int32
	MaxExponentDigits  int
}

ParseOptions controls accepted human-input syntax. Parse uses strict, locale-neutral defaults except for the Unicode minus sign.

type RoundingMode

type RoundingMode byte

RoundingMode controls how discarded fractional digits are handled.

const (
	// ToNearestEven rounds to the nearest value, with ties going to the even digit.
	ToNearestEven RoundingMode = iota
	// ToNearestAway rounds to the nearest value, with ties away from zero.
	ToNearestAway
	// ToNearestTowardZero rounds to the nearest value, with ties toward zero.
	ToNearestTowardZero
	// AwayFromZero rounds any discarded non-zero digit away from zero.
	AwayFromZero
	// TowardZero truncates discarded digits.
	TowardZero
	// TowardPositive rounds toward +infinity.
	TowardPositive
	// TowardNegative rounds toward -infinity.
	TowardNegative
)

func ParseRoundingMode

func ParseRoundingMode(text string) (RoundingMode, error)

ParseRoundingMode parses stable names and common finance aliases.

func (RoundingMode) MarshalJSON

func (m RoundingMode) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler as a stable string.

func (RoundingMode) MarshalText

func (m RoundingMode) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (RoundingMode) String

func (m RoundingMode) String() string

String returns a stable audit-friendly name for m.

func (*RoundingMode) UnmarshalJSON

func (m *RoundingMode) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler from a stable string.

func (*RoundingMode) UnmarshalText

func (m *RoundingMode) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

Directories

Path Synopsis
internal
qdecimalci command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL