pub struct Fixed(/* private fields */);Expand description
A fixed-point number: Q47.16 in an i64.
§Contract
- Resolution 2⁻¹⁶ ≈ 0.0000153; range ±2⁴⁷ ≈ ±1.4 × 10¹⁴.
- Every operation saturates on overflow, in every build profile, and
increments the thread’s
crate::saturationscounter when it does. Never wraps. Never differs between debug and release. - Multiplication and division round to nearest, ties away from zero.
Symmetric under negation, which the obvious implementation is not — see
Fixed::saturating_mul. - Total ordering.
Ord,EqandHashare derived from thei64, so this sorts, deduplicates and hashes the way an integer does and floats cannot.
§Why Q47.16 and not Q32.32
Because physics squares things. A squared value has to fit the type it is stored in, so the range that matters is not what is representable but what is squarable — the square root of the representable range:
| representable | squarable | |
|---|---|---|
| Q47.16 | ±1.4 × 10¹⁴ | ±1.2 × 10⁷ |
| Q32.32 | ±2.1 × 10⁹ | ±4.6 × 10⁴ |
Two hundred and fifty-six times the working room, for a resolution that is already finer than anything a game perceives.
Implementations§
Source§impl Fixed
impl Fixed
Sourcepub const fn from_int(value: i32) -> Self
pub const fn from_int(value: i32) -> Self
A whole number, exactly.
i32 rather than i64 so the shift cannot overflow: every i32
shifted left by 16 fits an i64 with room to spare, which makes this
total and lets it be const.
Sourcepub const fn from_ratio(numerator: i32, denominator: i32) -> Self
pub const fn from_ratio(numerator: i32, denominator: i32) -> Self
A ratio of two integers — how a value like 9.81 is written without a
float ever existing: Fixed::from_ratio(981, 100).
Rounds to nearest, ties away from zero, like Fixed::saturating_mul.
§Panics
If denominator is zero. A contract violation rather than a runtime
condition (D5): the arguments are almost always literals, so this
fails at the call site that wrote it, and in a const context it
fails at compile time.
Sourcepub fn abs(self) -> Self
pub fn abs(self) -> Self
Absolute value, saturating at Fixed::MAX for Fixed::MIN.
Sourcepub const fn clamp(self, low: Self, high: Self) -> Self
pub const fn clamp(self, low: Self, high: Self) -> Self
Constrained to [low, high].
§Panics
If low > high, which is a contract violation rather than a value to
interpret — the caller has said something they cannot mean.
Sourcepub fn saturating_mul(self, other: Self) -> Self
pub fn saturating_mul(self, other: Self) -> Self
Multiply, rounding to nearest with ties away from zero.
The rounding rule is load-bearing. The obvious implementation —
(a as i128 * b as i128) >> 16 — is an arithmetic shift, which rounds
toward negative infinity and is therefore asymmetric under negation:
(-a) * b and -(a * b) differ for some inputs. That is deterministic
and still wrong for physics, because a body moving left and the same
body moving right would accumulate different error. Rounding to nearest
with ties away from zero is symmetric, and halves the worst-case error
besides.
Saturates rather than wrapping, and counts when it does.
Sourcepub fn saturating_div(self, other: Self) -> Self
pub fn saturating_div(self, other: Self) -> Self
Divide, rounding to nearest with ties away from zero.
§Panics
If other is zero. Division by zero is a contract violation (D5), and
returning a sentinel would put a NaN-shaped value into a type whose
whole contract is that it has none.
Sourcepub fn sqrt(self) -> Self
pub fn sqrt(self) -> Self
The square root, floored to the representable value below the exact result.
Uses u128::isqrt, which is exact by its own contract, on a u128
intermediate — the shifted value needs 79 bits, so a 64-bit one would
be wrong rather than merely slower. Not a hand-rolled iteration: the
standard library’s is boring and already correct, and this is the one
kernel here with a non-trivial correctness argument.
§Panics
If self is negative. See Fixed::checked_sqrt for the form that
answers instead of refusing.
Sourcepub fn checked_sqrt(self) -> Option<Self>
pub fn checked_sqrt(self) -> Option<Self>
The square root, or None for a negative value.
Sourcepub const fn checked_add(self, other: Self) -> Option<Self>
pub const fn checked_add(self, other: Self) -> Option<Self>
Add, or None if the result would not fit.
Sourcepub const fn checked_sub(self, other: Self) -> Option<Self>
pub const fn checked_sub(self, other: Self) -> Option<Self>
Subtract, or None if the result would not fit.
Sourcepub const fn checked_div(self, other: Self) -> Option<Self>
pub const fn checked_div(self, other: Self) -> Option<Self>
Divide, or None for a zero divisor or a result that would not fit.
The form to reach for wherever a divisor comes from data rather than
from a literal — a ray direction, a difference of two positions, a
time of impact. Fixed::saturating_div asserts on zero because a
literal zero divisor is a programming error; a computed zero is an
ordinary value that geometry produces constantly, and asserting on it
would put a panic on a path that runs every frame.
Sourcepub const fn checked_mul(self, other: Self) -> Option<Self>
pub const fn checked_mul(self, other: Self) -> Option<Self>
Multiply, or None if the result would not fit.
const, and therefore not counted: a compile-time context has no
thread to count on, and a caller asking this question wants the answer
rather than a diagnostic.
Trait Implementations§
impl Copy for Fixed
impl Eq for Fixed
Source§impl Mul<Fixed> for Vec2
Scaled by a scalar. Saturating componentwise, like everything else here.
impl Mul<Fixed> for Vec2
Scaled by a scalar. Saturating componentwise, like everything else here.