fixed

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 2 Imported by: 0

README

fixed

fixed is a small Go package for signed Q32.32 arithmetic. Equal inputs produce the same result bits on every supported architecture.

A Q value uses one int64: 32 bits for the signed integer part and 32 bits for the fraction. This gives a resolution of 2⁻³² and a range from -2³¹ to 2³¹ - 2⁻³².

The module is pre-v1. Its import path may change before the first stable release. It requires Go 1.26.4 or newer.

Install

go get github.com/dhannyell/fixed

Quick start

package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	price := fixed.MustParse("6.25")
	quantity := fixed.FromRatio(3, 2)
	total := price.Mul(quantity)

	fmt.Println(total)       // 9.375
	fmt.Println(total.Raw()) // 40265318400
}

Why there is no float constructor

Floating-point input can already contain small differences caused by an earlier computation. The package cannot recover the intended exact value from those bits. For this reason, at least initially, support for float input is not planned, fixed accepts only explicit inputs:

  • FromInt for integers.
  • FromRatio for exact ratios.
  • MustParse for decimal literals.
  • FromRaw for an exact Q32.32 bit pattern.

String provides the inverse text boundary. It emits a canonical decimal representation, and every value satisfies:

fixed.MustParse(q.String()) == q

Arithmetic contract

The package uses one explicit rule for each operation:

Operation Result
Add, Sub Exact Q32.32 result, with saturation on overflow
Mul Product floored to Q32.32, with saturation on overflow
Div, FromRatio Quotient truncated toward zero, with saturation on overflow
Sqrt Square root floored to Q32.32
Round, MustParse Nearest representable value; exact halves round away from zero

Division by zero panics. Sqrt of a negative value also panics.

Saturated addition is not associative near the limits. Reordering a sum can therefore change its result. Accumulators should have enough headroom to avoid saturation.

Every saturation increments a process-wide atomic counter. SaturationCount provides diagnostics without changing any Q value or operation result.

Architecture

fixed is a leaf module. Its production code imports only math/bits and sync/atomic. This small dependency surface lets applications use the numeric type without importing unrelated systems.

The Q type is opaque. Constructors control how values enter the package, operations own saturation and rounding, and Raw is the boundary for exact bit access.

The Go implementation defines the bit-level contract. An independent implementation must preserve the rounding, saturation, and raw representation before it exchanges values with this package. A change to one of these rules is a semantic change, not an internal refactor.

Development

Run the standard checks before submitting a change:

go test ./...
go vet ./...
golangci-lint run ./...

See the package documentation for the API reference and the full behavioral contract.

License

fixed is available under the MIT License.

Documentation

Overview

Package fixed implements deterministic Q32.32 fixed-point arithmetic.

Q stores one signed value in an int64. The high 32 bits contain the signed integer part. The low 32 bits contain the fraction. The resolution is 2⁻³². The range is [-2³¹, 2³¹ - 2⁻³²]. Each operation produces the same bits on every supported architecture.

Overflow

An overflow saturates to MinValue or MaxValue. SaturationCount reports saturation events for diagnostics. The counter does not affect Q values or operation results.

Q.Div panics for a zero divisor. FromRatio panics for a zero denominator. Q.Sqrt panics for a negative input.

Saturated addition is not associative near the range limits. Do not reorder an accumulation. Use enough numeric headroom to prevent saturation.

Rounding

Q.Mul floors the 128-bit product when it converts the result to Q32.32. This operation matches an arithmetic right shift. Q.Div and FromRatio truncate toward zero. Q.Sqrt floors its result.

Q.Round and MustParse round to the nearest representable value. An exact half rounds away from zero.

Construction and text

Q is opaque. Construct values with FromInt, FromRatio, MustParse, or FromRaw. The package does not accept float values because a computed float can contain architecture-dependent bits.

Q.String returns the exact canonical decimal form. For every q, MustParse(q.String()) == q.

Compatibility contract

The raw representation, saturation rules, and rounding rules are part of the public contract. An independent implementation must reproduce these rules before it exchanges raw values with this package.

Dependencies

Production files import only math/bits and sync/atomic.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ResetSaturationCount

func ResetSaturationCount()

ResetSaturationCount zeroes the saturation counter.

func SaturationCount

func SaturationCount() uint64

SaturationCount reports the number of saturation events since the last reset.

Types

type Q

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

Q stores an opaque signed Q32.32 value. Its zero value is 0.

func FromInt

func FromInt(i int) Q

FromInt returns i as a Q value. It saturates outside [-2³¹, 2³¹-1].

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	fmt.Println(fixed.FromInt(3))
	fmt.Println(fixed.FromInt(-2))
}
Output:
3
-2

func FromRatio

func FromRatio(num, den int) Q

FromRatio returns num/den truncated toward zero. It saturates on overflow. It panics when den is zero.

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	// Use a ratio for exact fractional constants. There is no FromFloat.
	q := fixed.FromRatio(5, 2)
	fmt.Println(q)
}
Output:
2.5

func FromRaw

func FromRaw(raw int64) Q

FromRaw returns the Q value with the specified signed bit pattern.

func Half

func Half() Q

Half returns the fixed-point value 1/2.

func MaxValue

func MaxValue() Q

MaxValue returns the largest representable Q value, 2³¹ - 2⁻³².

func MinValue

func MinValue() Q

MinValue returns the smallest representable Q value, -2³¹.

func MustParse

func MustParse(s string) Q

MustParse parses a decimal literal. It rounds to the nearest Q value, with exact halves away from zero. It saturates outside the Q range and panics on malformed input.

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	fmt.Println(fixed.MustParse("6.25"))
	fmt.Println(fixed.MustParse("-0.001"))
}
Output:
6.25
-0.00099999993108212947845458984375

func One

func One() Q

One returns the fixed-point value 1.

func Zero

func Zero() Q

Zero returns the fixed-point value 0.

func (Q) Abs

func (q Q) Abs() Q

Abs returns the magnitude of q. Abs of MinValue saturates to MaxValue.

func (Q) Add

func (q Q) Add(o Q) Q

Add returns q+o. It saturates on overflow.

func (Q) Ceil

func (q Q) Ceil() Q

Ceil returns the smallest integer multiple of One that is not below q. It saturates when the result is outside the Q range.

func (Q) Clamp

func (q Q) Clamp(lo, hi Q) Q

Clamp returns q limited to [lo, hi]. It requires lo <= hi.

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	speed := fixed.FromInt(150)
	limited := speed.Clamp(fixed.Zero(), fixed.FromInt(100))
	fmt.Println(limited)
}
Output:
100

func (Q) Cmp

func (q Q) Cmp(o Q) int

Cmp returns -1 when q < o, 0 when q == o, and 1 when q > o.

func (Q) Div

func (q Q) Div(o Q) Q

Div returns q/o truncated toward zero. It saturates on overflow. It panics when o is zero.

func (Q) Eq

func (q Q) Eq(o Q) bool

Eq reports whether q == o.

func (Q) Floor

func (q Q) Floor() Q

Floor returns the largest integer multiple of One not above q.

func (Q) Int

func (q Q) Int() int

Int returns the integer part truncated toward zero.

func (Q) Less

func (q Q) Less(o Q) bool

Less reports whether q < o.

func (Q) Max

func (q Q) Max(o Q) Q

Max returns the larger of q and o.

func (Q) Min

func (q Q) Min(o Q) Q

Min returns the smaller of q and o.

func (Q) Mul

func (q Q) Mul(o Q) Q

Mul returns q*o. It floors the product to Q32.32 and saturates on overflow.

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	area := fixed.FromRatio(5, 2).Mul(fixed.FromInt(4))
	fmt.Println(area)
}
Output:
10

func (Q) Neg

func (q Q) Neg() Q

Neg returns -q. Neg of MinValue saturates to MaxValue.

func (Q) Raw

func (q Q) Raw() int64

Raw returns the signed Q32.32 bit pattern of q.

func (Q) Round

func (q Q) Round() Q

Round returns the nearest integer multiple of One. An exact half rounds away from zero. Round saturates when the result is outside the Q range.

func (Q) Sqrt

func (q Q) Sqrt() Q

Sqrt returns floor(sqrt(q)). It panics when q is negative. The result cannot overflow.

func (Q) String

func (q Q) String() string

String returns the exact canonical decimal form of q, such as "-6.25". For every q, MustParse(q.String()) == q. Use Raw for the exact bit pattern.

Example
package main

import (
	"fmt"

	"github.com/dhannyell/fixed"
)

func main() {
	q := fixed.FromRaw(1)
	text := q.String()
	fmt.Println(text)
	fmt.Println(fixed.MustParse(text).Eq(q))
}
Output:
0.00000000023283064365386962890625
true

func (Q) Sub

func (q Q) Sub(o Q) Q

Sub returns q-o. It saturates on overflow.

Jump to

Keyboard shortcuts

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