# Fixed point primitives
`dsp-fixedpoint` provides small `no_std` fixed-point primitives with explicit
integer storage and widening accumulators.
## Model
`Q<T, A, F>` stores a raw integer `T` and interprets it as scaled by `2^F`.
- `T` is the storage type.
- `A` is the widened accumulator type used for intermediate results.
- `F` is the number of fractional bits.
Type aliases cover the common signed, unsigned, and wrapping pairs:
`Q8/Q16/Q32/Q64`, `P8/P16/P32/P64`, `W8/W16/W32/W64`, and `V8/V16/V32/V64`.
## Construction
There are two construction modes:
- `from_int`, `from_f32`, and `from_f64` scale into the fixed-point domain.
- `from_bits` is the raw-representation constructor.
```rust
use dsp_fixedpoint::Q8;
let scaled = Q8::<4>::from_int(3);
let raw = Q8::<4>::from_bits(3 << 4);
assert_eq!(scaled, raw);
assert_eq!(raw.into_bits(), 48);
```
## Operator semantics
The crate keeps addition-like operators conservative and multiplication-like
operators efficient. `Q` means `Q<T, A, F>` unless shown otherwise.
| `Q<F> + Q<F>`, `Q<F> - Q<F>`, `Q<F> % Q<F>` | `Q<T, A, F>` | Requires the same `F`; use `.scale::<F1>()` explicitly when scales differ. |
| `Q * Q`, `Q / Q` | `Q<T, A, F>` | Preserves the left-hand scale and quantizes the fixed-point result. |
| `Q * T` | `Q<A, T, F>` | Widened raw-integer multiplication; same as `q.mul_wide(t)`. |
| `T * Q` | `T` | Applies `Q` as a gain to `T` and quantizes; same as `q.apply(t)`. |
| `Q / T` | `Q<T, A, F>` | Raw integer division of the stored representation. |
| `T / Q` | `T` | Divides by `Q` as a fixed-point gain and quantizes. |
| `Q *= Q`, `Q /= Q` | `Q<T, A, F>` | Assignment forms follow the left-hand scale. |
| `Q *= T`, `Q /= T` | `Q<T, A, F>` | Assignment forms operate on the raw stored representation. |
The mixed multiplication and division asymmetry is intentional: operand order
chooses whether the result remains wide or is quantized immediately. Use
`mul_wide()` and `apply()` when spelling that choice explicitly is clearer than
relying on operand order.
## Scale restrictions
`F` is an `i8`, but not every `i8` value is meaningful everywhere.
- `Q::<_, _, -128>::DELTA` is rejected at compile time.
- `Q::<_, _, F>::one()` and `Q::<_, _, F>::ONE` are rejected at compile time
when the mathematical value `1` is not exactly representable by the storage
type and scale.
```compile_fail
use dsp_fixedpoint::Q8;
use num_traits::One;
let _ = Q8::<7>::one();
```
## Serialization
With `feature = "serde"`, `Q` serializes transparently as its raw representation.
For lossy scaled values, use `dsp_fixedpoint::serde::as_f32` or
`dsp_fixedpoint::serde::as_f64`.
```rust,ignore
# use serde::{Deserialize, Serialize};
use dsp_fixedpoint::Q32;
#[derive(Deserialize, Serialize)]
struct Config {
#[serde(with = "dsp_fixedpoint::serde::as_f64")]
gain: Q32<3>,
}
```
## Ecosystem Traits
`Q` implements a small set of generic numeric traits where the semantics stay
clear:
- `Bounded` forwards to the raw storage bounds.
- `ToPrimitive`, `FromPrimitive`, and `NumCast` convert numeric values rather
than raw bits.
- `Signed` is implemented for signed `Q`/`W` families.
- `Hash` follows the raw representation.
`Signed` inherits `One`, so the existing representability restriction still
applies: operations that need an exact `1`, such as `signum()`, are only usable
when `F >= 0`.
## Formatting
Display shows decimal notation. `Binary/Octal/UpperHex/LowerHex` show dot
notation.
```rust
use dsp_fixedpoint::Q8;
assert_eq!(
format!("{:#b}", Q8::<3>::from_bits(0b01101001)),
"0b1101.001"
);
assert_eq!(format!("{:x}", Q8::<4>::from_bits(-0x14)), "-1.4");
```
## `defmt`
With `feature = "defmt"`, `Q` implements `defmt::Format` and logs as a decimal
value using `f32`. This keeps the target-side implementation compact for
embedded use. Exact radix-dot formatting remains available through the standard
`Binary`, `Octal`, and hex format traits.
## `bytemuck`
With `feature = "bytemuck"`, `Q` derives `Pod`, `Zeroable`, and
`TransparentWrapper`. That makes zero-copy buffer casts and wrapper conversions
available when the underlying storage and accumulator types also satisfy the
corresponding `bytemuck` bounds.