Documentation
¶
Overview ¶
Package fixed implements deterministic signed fixed-point arithmetic.
The package provides two formats without choosing a default. Q32 stores Q32.32 in an int64, with resolution 2⁻³² and range [-2³¹, 2³¹ - 2⁻³²]. Q16 stores Q16.16 in an int32, with resolution 2⁻¹⁶ and range [-2¹⁵, 2¹⁵ - 2⁻¹⁶]. Each operation produces the same bits on every supported architecture.
Overflow ¶
An overflow saturates to the minimum or maximum of the selected format. SaturationCount reports saturation events for diagnostics. The counter does not affect fixed-point values or operation results.
Q32.Div and Q16.Div panic for a zero divisor. Q32FromRatio and Q16FromRatio panic for a zero denominator. Q32.Sqrt and Q16.Sqrt panic 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 ¶
Q32.Mul floors the 128-bit product when it converts the result to Q32.32. Q16.Mul floors the exact 62-bit product when it converts the result to Q16.16. Both operations match an arithmetic right shift.
Q32.Div, Q16.Div, Q32FromRatio, and Q16FromRatio truncate toward zero. Q32.Sqrt and Q16.Sqrt floor their results. Q32.Round, Q16.Round, Q32MustParse, and Q16MustParse round to the nearest representable value. An exact half rounds away from zero.
Formats and conversion ¶
Q16.ToQ32 widens exactly and never saturates. Q32.ToQ16 floors to the Q16.16 grid and saturates outside the Q16 range. For any Q16 values a and b, a.Mul(b) equals a.ToQ32().Mul(b.ToQ32()).ToQ16(). The same identity does not hold for Div because division truncates toward zero and narrowing floors.
Construction and text ¶
Q32 and Q16 are opaque. Construct Q32 values with Q32FromInt, Q32FromRatio, Q32MustParse, or Q32FromRaw. Construct Q16 values with Q16FromInt, Q16FromRatio, Q16MustParse, or Q16FromRaw. The package does not accept float values because a computed float can contain architecture-dependent bits.
Q32.String and Q16.String return exact canonical decimal forms. For every value q, parsing q.String() with the constructor for its format returns q.
Angles ¶
Angles use turns. Q32One is a full revolution. The fractional bits of a Q32 map directly to the circle, so range reduction does not use pi and does not round.
SinTurns and CosTurns accept every Q32 value. They never panic or saturate, and their results stay in [-Q32One, Q32One]. They use a 1024-interval quarter-wave table. Table entries round to nearest, and linear interpolation floors. The maximum absolute error is 2⁻²⁰.
Atan2Turns returns an angle in (-1/2, 1/2] turns. It reduces the input to a ratio in [0, 1], truncates that ratio to Q32.32, and uses a 1024-interval table. Table entries round to nearest, and linear interpolation floors. Octant reconstruction is exact.
Vectors and rotations ¶
Vec2.LenSq composes scalar multiplication and addition, so it can saturate even when the length fits in Q32.32. Vec2.Len computes the length with a 128-bit intermediate and saturates only when the final length is out of range. Vec2.Normalize scales the components before it squares them, so intermediate underflow cannot turn a nonzero vector into the zero vector.
Rot stores a rotation as its sine and cosine. The zero Rot is invalid. Use RotIdentity or RotFromTurns to construct a rotation. Repeated composition can introduce rounding drift; Rot.Normalize restores unit length.
Batch operations ¶
BatchAdd16, BatchSub16, BatchMul16, and BatchClamp16 apply one operation across whole slices of Q16. Their results and saturation counts are identical to a loop over the scalar methods; the counter receives one update per call with the number of saturated elements. The destination may be the same slice as a source; any other overlap is undefined. BatchQ32FromQ16 and BatchQ16FromQ32 move whole slices across the format boundary and follow the conversion rules of Q16.ToQ32 and Q32.ToQ16.
Every build runs the scalar kernels. A build with GOEXPERIMENT=simd on Go 1.27 or later selects vector kernels at package initialization on amd64 with AVX2 and on arm64. BatchPath reports the active family. The selection changes speed, never bits.
Compatibility contract ¶
The two raw representations, their conversions, 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. The trigonometric raw outputs are also part of the contract. A compatible implementation may use a different representation, but it must produce the same output bits.
Dependencies ¶
The portable files import only math, math/bits, and sync/atomic. The math import provides hardware seeds; exact integer comparisons close every result, so floating point never decides a bit. Files behind the goexperiment.simd build tag also import unsafe and simd/archsimd; no default build reaches them. The unsafe import is confined to batch16_raw.go, which reinterprets a Q16 or Q32 slice as the raw words the vector loads take and asserts the layout at compile time.
Index ¶
- func BatchAdd16(dst, a, b []Q16)
- func BatchClamp16(dst, a []Q16, lo, hi Q16)
- func BatchMul16(dst, a, b []Q16)
- func BatchPath() string
- func BatchQ16FromQ32(dst []Q16, a []Q32)
- func BatchQ32FromQ16(dst []Q32, a []Q16)
- func BatchSub16(dst, a, b []Q16)
- func ResetSaturationCount()
- func SaturationCount() uint64
- type Q16
- func (q Q16) Abs() Q16
- func (q Q16) Add(o Q16) Q16
- func (q Q16) Ceil() Q16
- func (q Q16) Clamp(lo, hi Q16) Q16
- func (q Q16) Cmp(o Q16) int
- func (q Q16) Div(o Q16) Q16
- func (q Q16) Eq(o Q16) bool
- func (q Q16) Floor() Q16
- func (q Q16) Greater(o Q16) bool
- func (q Q16) Int() int
- func (q Q16) Less(o Q16) bool
- func (q Q16) Max(o Q16) Q16
- func (q Q16) Min(o Q16) Q16
- func (q Q16) Mul(o Q16) Q16
- func (q Q16) Neg() Q16
- func (q Q16) Raw() int32
- func (q Q16) Round() Q16
- func (q Q16) Sqrt() Q16
- func (q Q16) String() string
- func (q Q16) Sub(o Q16) Q16
- func (q Q16) ToQ32() Q32
- type Q32
- func Atan2Turns(y, x Q32) Q32
- func CosTurns(t Q32) Q32
- func Q32FromInt(i int) Q32
- func Q32FromRatio(num, den int) Q32
- func Q32FromRaw(raw int64) Q32
- func Q32Half() Q32
- func Q32MaxValue() Q32
- func Q32MinValue() Q32
- func Q32MustParse(s string) Q32
- func Q32One() Q32
- func Q32Zero() Q32
- func SinTurns(t Q32) Q32
- func (q Q32) Abs() Q32
- func (q Q32) Add(o Q32) Q32
- func (q Q32) Ceil() Q32
- func (q Q32) Clamp(lo, hi Q32) Q32
- func (q Q32) Cmp(o Q32) int
- func (q Q32) Div(o Q32) Q32
- func (q Q32) Eq(o Q32) bool
- func (q Q32) Floor() Q32
- func (q Q32) Greater(o Q32) bool
- func (q Q32) Int() int
- func (q Q32) Less(o Q32) bool
- func (q Q32) Max(o Q32) Q32
- func (q Q32) Min(o Q32) Q32
- func (q Q32) Mul(o Q32) Q32
- func (q Q32) Neg() Q32
- func (q Q32) Raw() int64
- func (q Q32) Round() Q32
- func (q Q32) Sqrt() Q32
- func (q Q32) String() string
- func (q Q32) Sub(o Q32) Q32
- func (q Q32) ToQ16() Q16
- type Rot
- type Vec2
- func (v Vec2) Add(o Vec2) Vec2
- func (v Vec2) Distance(o Vec2) Q32
- func (v Vec2) DistanceSq(o Vec2) Q32
- func (v Vec2) Div(s Q32) Vec2
- func (v Vec2) Dot(o Vec2) Q32
- func (v Vec2) Len() Q32
- func (v Vec2) LenSq() Q32
- func (v Vec2) Lerp(target Vec2, t Q32) Vec2
- func (v Vec2) Mul(s Q32) Vec2
- func (v Vec2) Normalize() Vec2
- func (v Vec2) Sub(o Vec2) Vec2
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BatchAdd16 ¶ added in v0.4.0
func BatchAdd16(dst, a, b []Q16)
BatchAdd16 stores a[i]+b[i] into dst. Each element saturates on overflow. All three slices must share one length. dst may be the same slice as a or b; no other overlap is allowed.
func BatchClamp16 ¶ added in v0.4.0
BatchClamp16 stores a[i] limited to [lo, hi] into dst. It requires lo <= hi. A clamp is not an overflow, so it records no saturation event. Both slices must share one length. dst may be the same slice as a; no other overlap is allowed.
func BatchMul16 ¶ added in v0.4.0
func BatchMul16(dst, a, b []Q16)
BatchMul16 stores a[i]*b[i] into dst. It floors each product to Q16.16 and saturates on overflow. All three slices must share one length. dst may be the same slice as a or b; no other overlap is allowed.
func BatchPath ¶ added in v0.4.0
func BatchPath() string
BatchPath reports the kernel family behind the batch functions on this host: "scalar", "avx2" or "neon". Every path produces the same bits.
func BatchQ16FromQ32 ¶ added in v0.4.0
BatchQ16FromQ32 stores a[i] floored to the Q16.16 grid into dst. Each element saturates outside the Q16 range. Both slices must share one length.
func BatchQ32FromQ16 ¶ added in v0.4.0
BatchQ32FromQ16 stores a[i] converted to Q32 into dst. The conversion is exact and never saturates. Both slices must share one length.
func BatchSub16 ¶ added in v0.4.0
func BatchSub16(dst, a, b []Q16)
BatchSub16 stores a[i]-b[i] into dst. Each element saturates on overflow. All three slices must share one length. dst may be the same slice as a or b; no other overlap is allowed.
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 Q16 ¶ added in v0.3.0
type Q16 struct {
// contains filtered or unexported fields
}
Q16 stores an opaque signed Q16.16 value. Its zero value is 0.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
// Q16 is the compact format. It converts to and from Q32.
price := fixed.Q16FromRatio(5, 2)
total := price.Mul(fixed.Q16FromInt(4))
fmt.Println(total)
fmt.Println(total.ToQ32().Eq(fixed.Q32FromInt(10)))
}
Output: 10 true
func Q16FromInt ¶ added in v0.3.0
Q16FromInt returns i as a Q16 value. It saturates outside [-2¹⁵, 2¹⁵-1].
func Q16FromRatio ¶ added in v0.3.0
Q16FromRatio returns num/den truncated toward zero. It saturates on overflow. It panics when den is zero.
func Q16FromRaw ¶ added in v0.3.0
Q16FromRaw returns the Q16 value with the specified signed bit pattern.
func Q16MaxValue ¶ added in v0.3.0
func Q16MaxValue() Q16
Q16MaxValue returns the largest representable Q16 value, 2¹⁵ - 2⁻¹⁶.
func Q16MinValue ¶ added in v0.3.0
func Q16MinValue() Q16
Q16MinValue returns the smallest representable Q16 value, -2¹⁵.
func Q16MustParse ¶ added in v0.3.0
Q16MustParse parses a decimal literal. It rounds to the nearest Q16 value, with exact halves away from zero. It saturates outside the Q16 range and panics on malformed input.
func (Q16) Abs ¶ added in v0.3.0
Abs returns the magnitude of q. Abs of the minimum saturates to the maximum.
func (Q16) Ceil ¶ added in v0.3.0
Ceil returns the smallest integer multiple of Q16One not below q. It saturates when the result is outside the Q16 range.
func (Q16) Div ¶ added in v0.3.0
Div returns q/o truncated toward zero. It saturates on overflow. It panics when o is zero.
func (Q16) Floor ¶ added in v0.3.0
Floor returns the largest integer multiple of Q16One not above q.
func (Q16) Mul ¶ added in v0.3.0
Mul returns q*o. It floors the product to Q16.16 and saturates on overflow.
func (Q16) Round ¶ added in v0.3.0
Round returns the nearest integer multiple of Q16One. An exact half rounds away from zero. Round saturates when the result is outside the Q16 range.
func (Q16) Sqrt ¶ added in v0.3.0
Sqrt returns floor(sqrt(q)). It panics when q is negative. The result cannot overflow.
func (Q16) String ¶ added in v0.3.0
String returns the exact canonical decimal form of q. The widening conversion is exact, so the Q32 formatter emits the same value.
type Q32 ¶ added in v0.3.0
type Q32 struct {
// contains filtered or unexported fields
}
Q32 stores an opaque signed Q32.32 value. Its zero value is 0.
func Atan2Turns ¶ added in v0.2.1
Atan2Turns returns the angle of (x, y) in (-1/2, 1/2] turns. Atan2Turns(Zero(), Zero()) returns Zero. It never panics or saturates. The unit ratio is truncated to Q32.32, table entries round to nearest, and linear interpolation floors. The maximum absolute error is 2⁻²⁰ turn.
func CosTurns ¶ added in v0.2.1
CosTurns returns the cosine of t in turns. It uses the same rules as SinTurns with a quarter-turn shift.
func Q32FromInt ¶ added in v0.3.0
Q32FromInt returns i as a Q32 value. It saturates outside [-2³¹, 2³¹-1].
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
fmt.Println(fixed.Q32FromInt(3))
fmt.Println(fixed.Q32FromInt(-2))
}
Output: 3 -2
func Q32FromRatio ¶ added in v0.3.0
Q32FromRatio 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.Q32FromRatio(5, 2)
fmt.Println(q)
}
Output: 2.5
func Q32FromRaw ¶ added in v0.3.0
Q32FromRaw returns the Q32 value with the specified signed bit pattern.
func Q32MaxValue ¶ added in v0.3.0
func Q32MaxValue() Q32
Q32MaxValue returns the largest representable Q32 value, 2³¹ - 2⁻³².
func Q32MinValue ¶ added in v0.3.0
func Q32MinValue() Q32
Q32MinValue returns the smallest representable Q32 value, -2³¹.
func Q32MustParse ¶ added in v0.3.0
Q32MustParse parses a decimal literal. It rounds to the nearest Q32 value, with exact halves away from zero. It saturates outside the Q32 range and panics on malformed input.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
fmt.Println(fixed.Q32MustParse("6.25"))
fmt.Println(fixed.Q32MustParse("-0.001"))
}
Output: 6.25 -0.00099999993108212947845458984375
func SinTurns ¶ added in v0.2.1
SinTurns returns the sine of t, where One is a full revolution. It uses only the fractional part of t, accepts every Q32 value, and returns a value in [-One, One] without saturation.
The 1024-interval quarter-wave table rounds entries to nearest. Linear interpolation floors. The maximum absolute error is 2⁻²⁰.
func (Q32) Abs ¶ added in v0.3.0
Abs returns the magnitude of q. Abs of MinValue saturates to MaxValue.
func (Q32) Ceil ¶ added in v0.3.0
Ceil returns the smallest integer multiple of One that is not below q. It saturates when the result is outside the Q32 range.
func (Q32) Clamp ¶ added in v0.3.0
Clamp returns q limited to [lo, hi]. It requires lo <= hi.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
speed := fixed.Q32FromInt(150)
limited := speed.Clamp(fixed.Q32Zero(), fixed.Q32FromInt(100))
fmt.Println(limited)
}
Output: 100
func (Q32) Div ¶ added in v0.3.0
Div returns q/o truncated toward zero. It saturates on overflow. It panics when o is zero.
func (Q32) Mul ¶ added in v0.3.0
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.Q32FromRatio(5, 2).Mul(fixed.Q32FromInt(4))
fmt.Println(area)
}
Output: 10
func (Q32) Round ¶ added in v0.3.0
Round returns the nearest integer multiple of One. An exact half rounds away from zero. Round saturates when the result is outside the Q32 range.
func (Q32) Sqrt ¶ added in v0.3.0
Sqrt returns floor(sqrt(q)). It panics when q is negative. The result cannot overflow.
func (Q32) String ¶ added in v0.3.0
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.Q32FromRaw(1)
text := q.String()
fmt.Println(text)
fmt.Println(fixed.Q32MustParse(text).Eq(q))
}
Output: 0.00000000023283064365386962890625 true
type Rot ¶ added in v0.2.1
type Rot struct {
Sin, Cos Q32
}
Rot is a 2D rotation stored as its sine and cosine.
The zero Rot is not a valid rotation; start from RotIdentity or RotFromTurns.
func RotFromTurns ¶ added in v0.2.1
RotFromTurns returns the rotation by the angle t in turns. It shares the kernel and the contract of SinTurns and CosTurns.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
// A quarter turn sends (1, 0) to (0, 1) exactly.
r := fixed.RotFromTurns(fixed.Q32FromRatio(1, 4))
v := r.Apply(fixed.Vec2{X: fixed.Q32One(), Y: fixed.Q32Zero()})
fmt.Println(v.X, v.Y)
}
Output: 0 1
func RotIdentity ¶ added in v0.2.1
func RotIdentity() Rot
RotIdentity returns the rotation by zero turns.
func (Rot) Inv ¶ added in v0.2.1
Inv returns the inverse rotation. For a unit rotation the inverse is the conjugate, so no division is needed.
type Vec2 ¶ added in v0.2.1
type Vec2 struct {
X, Y Q32
}
Vec2 is a 2D vector of Q32 components.
func (Vec2) DistanceSq ¶ added in v0.2.1
DistanceSq returns the squared distance between v and o.
func (Vec2) LenSq ¶ added in v0.2.1
LenSq returns the squared length of the vector. It uses the scalar multiplication and addition rules, including saturation.
func (Vec2) Lerp ¶ added in v0.2.1
Lerp linearly interpolates between v and target by t. t is not clamped.
func (Vec2) Normalize ¶ added in v0.2.1
Normalize returns a unit vector with the same direction as v. The zero vector returns the zero vector.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
v := fixed.Vec2{X: fixed.Q32Zero(), Y: fixed.Q32FromInt(-7)}
u := v.Normalize()
fmt.Println(u.X, u.Y)
}
Output: 0 -1