Documentation
¶
Overview ¶
Package fixed implements deterministic signed fixed-point arithmetic.
The package provides three 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⁻¹⁶]. Q48 stores Q48.16 in an int64, with resolution 2⁻¹⁶ and range [-2⁴⁷, 2⁴⁷ - 2⁻¹⁶]; it is the accumulator for Q16 products. 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. Build with -tags=fixed_satcounter to add diagnostic counting, including local batch counts. Without it SaturationCountingEnabled is false, SaturationCount returns zero and ResetSaturationCount is a no-op. Saturation and rounding of numeric results are unchanged.
Div panics for a zero divisor in every format. Q32FromRatio, Q16FromRatio, and Q48FromRatio panic for a zero denominator. Sqrt panics for a negative input in every format.
Saturated addition is not associative near the range limits. Do not reorder an accumulation. Use enough numeric headroom to prevent saturation.
Rounding ¶
Q32.Mul and Q48.Mul floor the 128-bit product when they convert the result to their format. Q16.Mul floors the exact 62-bit product when it converts the result to Q16.16. All three operations match an arithmetic right shift. Q48.MulAdd16 floors the exact Q16 product to Q48.16 and then adds it.
Div and FromRatio truncate toward zero in every format. Sqrt floors its result in every format. Round and MustParse round to the nearest representable value in every format. An exact half rounds away from zero.
Formats and conversion ¶
A conversion changes the fraction grid, the integer range, or both. Each change follows one rule:
- A finer fraction grid is exact. A coarser fraction grid floors.
- A wider integer range never saturates. A narrower integer range saturates outside the target range.
Q16.ToQ32 and Q16.ToQ48 widen both. They are exact and never saturate. Q32.ToQ16 narrows both. It floors and saturates. Q16 and Q48 share one fraction grid, so Q48.ToQ16 only saturates and Q32.ToQ48 only floors. Q48.ToQ32 moves in both directions: the grid gets finer, so it is exact, and the integer range shrinks to 32 bits, so it saturates.
Int returns the integer part of a value. Q32.Int and Q16.Int return an int, because their integer range fits a 32-bit int. Q48.Int returns an int64, because its integer range does not. For the same reason Q48FromInt and Q48FromRatio take int64 arguments.
For any Q16 values a and b, a.Mul(b) equals a.ToQ32().Mul(b.ToQ32()).ToQ16() and also equals Q48Zero().MulAdd16(a, b).ToQ16(). The same identity does not hold for Div because division truncates toward zero and narrowing floors.
Accumulation ¶
A Q16 product has at most 32 integer bits. Q48.MulAdd16 stores it with 16 bits of headroom, so a sum of up to 2¹⁶ products of full-range Q16 values cannot saturate. Narrow the sum with Q48.ToQ16 only when the value is stored, and check SaturationCount when the term count is not bounded.
Construction and text ¶
Q32, Q16, and Q48 are opaque. Construct Q32 values with Q32FromInt, Q32FromRatio, Q32MustParse, or Q32FromRaw. Q16 and Q48 have the same four constructors under their own prefixes. The package does not accept float values because a computed float can contain architecture-dependent bits.
String returns the exact canonical decimal form in every format. 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. The negative x axis returns +1/2; quantization just below it can return -1/2. It reduces the input to a ratio in [0, 1], truncates it 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. Vec2.Lerp composes Sub, Mul, and Add, including intermediate saturation. Keep those intermediates in range to preserve the interpolation endpoints.
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. Rot.Inv is the conjugate and is an inverse for a unit rotation; Rot.InvNormalized normalizes first when drift may be present.
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.
BatchDot16 sums Q16 products into one Q48 in a fixed order: element i joins partial sum i mod 8, and the eight partials reduce as a balanced tree. Without saturation this equals a loop over Q48.MulAdd16; with saturation the order decides the bits, so every kernel keeps it. BatchQ48Mul16 scales a Q48 slice by a Q16 slice with the rules of Q48.Mul16.
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. On arm64 only the Q16 functions have vector kernels; BatchDot16 and BatchQ48Mul16 stay scalar there. BatchPath reports the active family. The selection changes speed, never bits.
Compatibility contract ¶
The three 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 ¶
- Constants
- 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 BatchQ48Mul16(dst, q []Q48, f []Q16)
- func BatchSub16(dst, a, b []Q16)
- func LanePath() string
- func LanesAvailable() bool
- func ResetSaturationCount()
- func SaturationCount() uint64
- type Lane16
- func (a Lane16) Add(b Lane16) Lane16
- func (a Lane16) AddWrap(b Lane16) Lane16
- func (a Lane16) Equals(b Lane16) Mask16
- func (a Lane16) Greater(b Lane16) Mask16
- func (a Lane16) Max(b Lane16) Lane16
- func (a Lane16) Min(b Lane16) Lane16
- func (a Lane16) Mul(b Lane16) Lane16
- func (a Lane16) MulAdd(b, c Lane16) Lane16
- func (a Lane16) MulRound(b Lane16) Lane16
- func (a Lane16) MulSub(b, c Lane16) Lane16
- func (a Lane16) Neg() Lane16
- func (a Lane16) ScaleDown(s Shift16) Lane16
- func (a Lane16) ScaleDownRound(s Shift16) Lane16
- func (a Lane16) ScaleUp(s Shift16) Lane16
- func (a Lane16) Store(p *[LaneWidth]Q16)
- func (a Lane16) Sub(b Lane16) Lane16
- func (a Lane16) SubWrap(b Lane16) Lane16
- func (a Lane16) SymClamp(limit Lane16) Lane16
- func (a Lane16) ToLane48() Lane48
- type Lane48
- func (a Lane48) Add(b Lane48) Lane48
- func (a Lane48) AddWrap(b Lane48) Lane48
- func (a Lane48) MulAdd16(b, c Lane16) Lane48
- func (a Lane48) MulAdd16Round(b, c Lane16) Lane48
- func (a Lane48) Store(p *[LaneWidth]Q48)
- func (a Lane48) Sub(b Lane48) Lane48
- func (a Lane48) SubWrap(b Lane48) Lane48
- func (a Lane48) ToLane16() Lane16
- type Mask16
- 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) MulRound(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
- func (q Q16) ToQ48() Q48
- 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
- func (q Q32) ToQ16Round() Q16
- func (q Q32) ToQ48() Q48
- func (q Q32) ToQ48Round() Q48
- type Q48
- func (q Q48) Abs() Q48
- func (q Q48) Add(o Q48) Q48
- func (q Q48) Ceil() Q48
- func (q Q48) Clamp(lo, hi Q48) Q48
- func (q Q48) Cmp(o Q48) int
- func (q Q48) Div(o Q48) Q48
- func (q Q48) Eq(o Q48) bool
- func (q Q48) Floor() Q48
- func (q Q48) Greater(o Q48) bool
- func (q Q48) Int() int64
- func (q Q48) Less(o Q48) bool
- func (q Q48) Max(o Q48) Q48
- func (q Q48) Min(o Q48) Q48
- func (q Q48) Mul(o Q48) Q48
- func (q Q48) Mul16(f Q16) Q48
- func (q Q48) MulAdd16(a, b Q16) Q48
- func (q Q48) MulAdd16Round(a, b Q16) Q48
- func (q Q48) Neg() Q48
- func (q Q48) Raw() int64
- func (q Q48) Round() Q48
- func (q Q48) Sqrt() Q48
- func (q Q48) String() string
- func (q Q48) Sub(o Q48) Q48
- func (q Q48) ToQ16() Q16
- func (q Q48) ToQ32() Q32
- type Rot
- type Shift16
- 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 ¶
const LaneWidth = 4
LaneWidth is the number of values in a lane type on this build path.
const SaturationCountingEnabled = false
SaturationCountingEnabled reports whether this build records saturation events. Build with -tags=fixed_satcounter to enable diagnostic counting. Arithmetic still saturates and produces the same result bits in either mode.
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 Q16 batch functions on this host: "scalar", "avx2" or "neon". The Q48 batch functions follow "avx2"; on "neon" they run their scalar kernels. 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 BatchQ48Mul16 ¶ added in v0.6.0
BatchQ48Mul16 stores q[i]*f[i] into dst. It floors each product to Q48.16 and saturates on overflow, as Q48.Mul16 does. All three slices must share one length. dst may be the same slice as q; no other overlap is allowed.
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 LanePath ¶ added in v0.8.0
func LanePath() string
LanePath reports the compiled lane implementation: "avx2", "neon", or "generic". Use LanesAvailable before using the amd64 SIMD implementation.
func LanesAvailable ¶ added in v0.8.0
func LanesAvailable() bool
LanesAvailable reports whether lane operations may be called. It is false only in an amd64 SIMD build on a CPU without AVX2. Calling any lane operation in that case is undefined. Other build paths always return true.
func ResetSaturationCount ¶
func ResetSaturationCount()
ResetSaturationCount has no effect unless the build enables counting.
func SaturationCount ¶
func SaturationCount() uint64
SaturationCount returns zero unless the build enables counting.
Types ¶
type Lane16 ¶ added in v0.8.0
type Lane16 struct {
// contains filtered or unexported fields
}
Lane16 holds LaneWidth Q16 values for register-resident lane-wise work. Its methods produce the same bits as the corresponding scalar operations. Mul rounds down with the BatchMul16 arithmetic-shift rule, which is also the Q16.Mul rule. Each saturated lane adds one event to SaturationCount when counting is enabled. MulAdd and MulSub execute two operations and count the multiplication and the addition or subtraction separately.
In an amd64 SIMD build, callers must check LanesAvailable before constructing or using a Lane16. Calling a lane operation without AVX2 is undefined.
The value is opaque: use the methods; the layout differs by path.
func BlendLane16 ¶ added in v0.8.0
BlendLane16 selects a where m is set and b where m is clear.
func LoadLane16 ¶ added in v0.8.0
LoadLane16 loads LaneWidth Q16 values from p.
func SplatLane16 ¶ added in v0.8.0
SplatLane16 returns a Lane16 with every lane set to q.
func (Lane16) AddWrap ¶ added in v1.0.0
AddWrap returns a+b lane-wise without overflow detection. A sum outside Q16 wraps and records no event. The caller must bound both operands; Add is the checked form.
func (Lane16) Mul ¶ added in v0.8.0
Mul returns a*b lane-wise, rounded down to Q16.16 and saturated.
func (Lane16) MulAdd ¶ added in v0.8.0
MulAdd returns a.Add(b.Mul(c)). It never fuses the two operations.
func (Lane16) MulRound ¶ added in v1.0.0
MulRound returns a*b lane-wise, rounded to the nearest Q16.16 step with exact ties toward positive infinity, and saturated.
func (Lane16) MulSub ¶ added in v0.8.0
MulSub returns a.Sub(b.Mul(c)). It never fuses the two operations.
func (Lane16) Neg ¶ added in v1.0.0
Neg returns -a lane-wise, with Q16 saturation. It subtracts from zero rather than multiplying by -1, which costs a widening product on the SIMD paths. The two agree in every lane, saturation included.
func (Lane16) ScaleDown ¶ added in v1.0.0
ScaleDown returns a divided by two raised to the matching lane of s. It rounds toward negative infinity, which is the Mul rule, and it cannot overflow or saturate.
For an amount up to 16 the result is the same bits as Mul by the Q16 value of two raised to minus that amount. Past 16 that value is not on the Q16 grid and the two disagree: Mul gives zero, ScaleDown keeps shifting.
func (Lane16) ScaleDownRound ¶ added in v1.0.0
ScaleDownRound returns a divided by two raised to the matching lane of s, rounded to the nearest step with exact ties toward positive infinity. It cannot overflow or saturate.
For an amount up to 16 the result is the same bits as MulRound by the Q16 value of two raised to minus that amount. ScaleDown is the truncating form and costs one instruction, so prefer it when the rounding does not matter.
func (Lane16) ScaleUp ¶ added in v1.0.0
ScaleUp returns a multiplied by two raised to the matching lane of s, with Q16 saturation. ScaleDown cannot overflow; this direction can, and a lane past the Q16 range clamps to the nearer limit and records an event.
For an amount up to 14 the result is the same bits as Mul by the Q16 value of two raised to that amount. Two to the fifteenth is already off the Q16 grid, so past 14 only the shift keeps going.
func (Lane16) SubWrap ¶ added in v1.0.0
SubWrap returns a-b lane-wise without overflow detection. A difference outside Q16 wraps and records no event. The caller must bound both operands; Sub is the checked form.
type Lane48 ¶ added in v0.8.0
type Lane48 struct {
// contains filtered or unexported fields
}
Lane48 holds LaneWidth Q48 values for register-resident accumulation. Its methods produce the same bits and saturation events as the corresponding scalar Q48 operations applied LaneWidth times.
The value is opaque: use the methods; the layout differs by path.
func LoadLane48 ¶ added in v0.8.0
LoadLane48 loads LaneWidth Q48 values from p.
func SplatLane48 ¶ added in v0.8.0
SplatLane48 returns a Lane48 with every lane set to q.
func (Lane48) AddWrap ¶ added in v1.0.0
AddWrap returns a+b lane-wise without overflow detection. A sum outside Q48 wraps and records no event. The caller must bound both operands; Add is the checked form.
func (Lane48) MulAdd16 ¶ added in v0.8.0
MulAdd16 adds the exact b*c Q16 product to each Q48 accumulator lane. Each product is rounded down to the shared Q48.16 grid before the saturating add.
func (Lane48) MulAdd16Round ¶ added in v1.0.0
MulAdd16Round rounds each Q16 product to the nearest Q48.16 step with exact ties toward positive infinity before the saturating lane-wise add.
type Mask16 ¶ added in v0.8.0
type Mask16 struct {
// contains filtered or unexported fields
}
Mask16 holds LaneWidth comparison results. Each lane is all ones when set and all zeros when clear.
The value is opaque: use the methods; the layout differs by path.
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) MulRound ¶ added in v1.0.0
MulRound returns q*o rounded to the nearest Q16.16 step, with exact ties toward positive infinity. It 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. The negative x axis returns +1/2; quantization just below it can return -1/2. 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
func (Q32) ToQ16 ¶ added in v0.3.0
ToQ16 floors q to the Q16.16 grid and saturates outside the Q16 range. Flooring matches the rounding of Mul.
func (Q32) ToQ16Round ¶ added in v1.0.0
ToQ16Round narrows q to the nearest Q16.16 step, with exact ties toward positive infinity. It saturates outside the Q16 range.
func (Q32) ToQ48Round ¶ added in v1.0.0
ToQ48Round narrows q to the nearest Q48.16 step, with exact ties toward positive infinity. It never saturates.
type Q48 ¶ added in v0.5.0
type Q48 struct {
// contains filtered or unexported fields
}
Q48 stores an opaque signed Q48.16 value. Its zero value is 0. Q48 shares the fraction grid of Q16, so it accumulates Q16 products with 16 bits of integer headroom.
func BatchDot16 ¶ added in v0.6.0
BatchDot16 returns the sum of a[i]*b[i] as Q48. Each product is exact and floored to the shared grid; each addition saturates. Both slices must share one length.
The summation order is fixed: element i joins partial sum i mod 8, and the eight partials then reduce as a balanced tree. Without saturation this equals a loop over Q48.MulAdd16. With saturation the order decides the result, so every kernel follows this one order and the bits match on every host.
func Q48FromInt ¶ added in v0.5.0
Q48FromInt returns i as a Q48 value. It saturates outside [-2⁴⁷, 2⁴⁷-1]. The integer range exceeds a 32-bit int, so the argument is an int64.
func Q48FromRatio ¶ added in v0.5.0
Q48FromRatio returns num/den truncated toward zero. It saturates on overflow. It panics when den is zero.
func Q48FromRaw ¶ added in v0.5.0
Q48FromRaw returns the Q48 value with the specified signed bit pattern.
func Q48MaxValue ¶ added in v0.5.0
func Q48MaxValue() Q48
Q48MaxValue returns the largest representable Q48 value, 2⁴⁷ - 2⁻¹⁶.
func Q48MinValue ¶ added in v0.5.0
func Q48MinValue() Q48
Q48MinValue returns the smallest representable Q48 value, -2⁴⁷.
func Q48MustParse ¶ added in v0.5.0
Q48MustParse parses a decimal literal. It rounds to the nearest Q48 value, with exact halves away from zero. It saturates outside the Q48 range and panics on malformed input.
func (Q48) Abs ¶ added in v0.5.0
Abs returns the magnitude of q. Abs of MinValue saturates to MaxValue.
func (Q48) Ceil ¶ added in v0.5.0
Ceil returns the smallest integer multiple of Q48One not below q. It saturates when the result is outside the Q48 range.
func (Q48) Div ¶ added in v0.5.0
Div returns q/o truncated toward zero. It saturates on overflow. It panics when o is zero.
func (Q48) Floor ¶ added in v0.5.0
Floor returns the largest integer multiple of Q48One not above q.
func (Q48) Int ¶ added in v0.5.0
Int returns the integer part truncated toward zero. The integer part does not fit a 32-bit int, so the result is an int64 on every architecture.
func (Q48) Mul ¶ added in v0.5.0
Mul returns q*o. It floors the product to Q48.16 and saturates on overflow.
func (Q48) Mul16 ¶ added in v0.6.0
Mul16 returns q*f for a Q16 factor. It floors the product to Q48.16 and saturates on overflow; the bits equal q.Mul(f.ToQ48()). The sign corrections are branch-free so the method stays inside the inlining budget.
func (Q48) MulAdd16 ¶ added in v0.5.0
MulAdd16 returns q + a*b. The Q16 product is exact in 64 bits. The sum floors it to the Q48.16 grid and saturates on overflow.
Example ¶
package main
import (
"fmt"
"github.com/dhannyell/fixed"
)
func main() {
// Q48 accumulates Q16 products that would saturate Q16 on their own.
x := fixed.Q16FromInt(200)
var acc fixed.Q48
for range 4 {
acc = acc.MulAdd16(x, x) // 4 × 40000 exceeds the Q16 range of 32768.
}
fmt.Println(acc)
fmt.Println(acc.ToQ16().Eq(fixed.Q16MaxValue()))
}
Output: 160000 true
func (Q48) MulAdd16Round ¶ added in v1.0.0
MulAdd16Round returns q + a*b, with the product rounded to the nearest Q48.16 step and exact ties toward positive infinity. MulAdd16 floors the product instead. The sum saturates on overflow either way.
func (Q48) Round ¶ added in v0.5.0
Round returns the nearest integer multiple of Q48One. An exact half rounds away from zero. Round saturates when the result is outside the Q48 range.
func (Q48) Sqrt ¶ added in v0.5.0
Sqrt returns floor(sqrt(q)). It panics when q is negative. The result cannot overflow.
func (Q48) String ¶ added in v0.5.0
String returns the exact canonical decimal form of q. For every q, Q48MustParse(q.String()) == q. Use Raw for the exact bit pattern.
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 conjugate of r. It is the inverse when r has unit length. Use InvNormalized when accumulated rounding drift may have changed the length.
func (Rot) InvNormalized ¶ added in v0.6.0
InvNormalized normalizes r and returns its inverse. A zero r returns the identity, following Normalize.
type Shift16 ¶ added in v1.0.0
type Shift16 struct {
// contains filtered or unexported fields
}
Shift16 holds LaneWidth shift amounts, one for each lane of a Lane16.
The value is opaque: build it with SplatShift16 or LoadShift16.
func LoadShift16 ¶ added in v1.0.0
LoadShift16 loads LaneWidth shift amounts from p, with the SplatShift16 rule for an amount above 31. It widens every amount, so it is not a single load.
func SplatShift16 ¶ added in v1.0.0
SplatShift16 returns a Shift16 with every lane set to n. An amount above 31 is stored as 31; both shift a lane to zero, or to -1 raw when it is negative.
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. Sub, Mul, and Add each apply their scalar saturation rules. Intermediate saturation can change the endpoint at t=1 and is counted even at t=0. Keep the intermediates in range when endpoints must be exact.
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
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
fixedtrace
command
Command fixedtrace writes one text trace per operation.
|
Command fixedtrace writes one text trace per operation. |
|
internal
|
|
|
gentable
command
Command gentable writes the lookup tables used by the trigonometric functions.
|
Command gentable writes the lookup tables used by the trigonometric functions. |