Python Fixed-Point Arithmetic API¶
The doppler.arith module is the saturating fixed-point toolkit: elementwise
Q15 (1.15, int16) and Q8 (1.7, int8) add / subtract / multiply, fractional
dot products, saturating shifts, and small running accumulators. Every operation
saturates on overflow rather than wrapping, matching how fixed-point DSP
hardware and the rest of doppler's Q-format paths behave.
Source:
src/doppler/arith/__init__.py
Fixed-point formats¶
| Format | Storage | Range | Resolution | One-liner |
|---|---|---|---|---|
| Q15 | int16 |
[-1, +1) |
2⁻¹⁵ |
1.0 → 32767, -1.0 → -32768 |
| Q8 | int8 |
[-1, +1) |
2⁻⁷ |
1.0 → 127, -1.0 → -128 |
A Q15 value v represents the real number v / 32768. Multiplication is the
fractional product ((a·b) >> 15), so 0.5 × 0.5 = 0.25; addition saturates so
0.5 + 0.5 reads the codec ceiling 32767 (≈ +1.0), never wrapping to a
negative.
Examples¶
Saturating Q15 elementwise ops¶
import numpy as np
from doppler.arith import add_q15, mul_q15
a = np.array([16384, 16384, -32768], dtype=np.int16) # 0.5, 0.5, -1.0
b = np.array([16384, 32767, 16384], dtype=np.int16) # 0.5, ~1.0, 0.5
add_q15(a, b) # array([32767, 32767, -16384]) -> 0.5+0.5 saturates to +1
mul_q15(a, b) # array([ 8192, 16384, -16384]) -> 0.5*0.5 = 0.25
Fractional dot product¶
dot_q15 accumulates the Q15 products in a wide integer and returns a Python
int, so a long inner product never overflows mid-sum.
from doppler.arith import dot_q15
taps = np.array([ 8192, 8192, 8192, 8192], dtype=np.int16) # 0.25 each
sample = np.array([32767, 16384, -16384, 0], dtype=np.int16)
acc = dot_q15(taps, sample) # wide integer accumulator (no overflow)
Saturating shifts¶
from doppler.arith import shl_q15, shr_q15
x = np.array([16384, -32768], dtype=np.int16) # 0.5, -1.0
shl_q15(x, 1) # array([32767, -32768]) -> 0.5<<1 saturates to +1, -1<<1 to -1
shr_q15(x, 2) # array([ 4096, -8192]) -> arithmetic right shift
Running Q15 / Q8 accumulators¶
AccQ15 / AccQ8 keep a 64-bit running sum across calls — drive them
sample-by-sample (step), in bulk (steps), or as a multiply-accumulate
(madd), then read with get() (peek) or dump() (read-and-reset). The wide
register means a long stream cannot overflow.
from doppler.arith import AccQ15
acc = AccQ15()
acc.steps(np.array([8192, 8192, 8192], dtype=np.int16)) # accumulate a block
acc.madd(np.array([16384], dtype=np.int16), # acc += sum(a*b)
np.array([16384], dtype=np.int16))
running = acc.get() # peek without resetting
final = acc.dump() # read and reset to zero
Accumulators¶
AccQ15
¶
Allocate and initialise an AccQ15 accumulator. The accumulator starts at the supplied initial value and may be driven sample-by-sample (step), in bulk (steps), or via multiply-accumulate (madd). The internal register is a 64-bit signed integer so it will not overflow in any realistic DSP workload.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
acc
|
int
|
acc state variable. |
0
|
Examples:
reset
¶
Reset the accumulator to zero, mirroring the post-create state. Does not re-initialise to the constructor's acc value — always resets to zero, matching the default initial state for a clean sweep.
Examples:
step
¶
Accumulate one Q15 sample into the running total. The sample is sign-extended to 64 bits before addition, ensuring that negative samples subtract correctly from the accumulator without wrap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
Q15 input sample (int16_t, range |
required |
Examples:
steps
¶
Accumulate a contiguous block of Q15 samples. Equivalent to calling step() n times but faster for large arrays because the loop can be auto-vectorised by the compiler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[int16]
|
Input. |
required |
Examples:
get
¶
dump
¶
Return the accumulated value and reset to zero.
Returns:
| Type | Description |
|---|---|
int
|
Accumulator value before the reset (int64_t). |
Examples:
madd
¶
Multiply-accumulate: acc += sum(a[i] * b[i]) for i in [0, len(a)). Uses AVX2 when available.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
First input array (int16_t). |
required |
b
|
NDArray[int16]
|
Second input array (int16_t), same length as a. |
required |
Examples:
state_bytes
¶
Size in bytes of this object's serialized state.
The exact length get_state returns and set_state requires. It
depends on how the object was constructed (state arrays are sized at
construction), so read it from the instance rather than assuming a
constant.
Raises RuntimeError if the AccQ15 has already been destroyed.
Returns:
| Type | Description |
|---|---|
int
|
Byte length of one serialized state blob. |
get_state
¶
Serialize this object's mutable state to bytes.
Captures exactly the state that evolves as the object runs, so a blob taken now and restored later resumes from this point. Construction parameters are not included: restore into an object built the same way.
The blob is opaque and always state_bytes() long. Its layout is an
implementation detail of the C core and is not a stable format across
builds.
Raises RuntimeError if the AccQ15 has already been destroyed.
Returns:
| Type | Description |
|---|---|
bytes
|
Opaque snapshot, |
set_state
¶
Restore mutable state from a get_state() blob.
Overwrites the live state in place; the object keeps the parameters it
was constructed with. Length is validated against state_bytes()
before the blob is handed to the C core, and the core may reject it as
well.
Raises TypeError if blob is not bytes, ValueError if its
length differs from state_bytes() or the core rejects it, and
RuntimeError if the AccQ15 has already been destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blob
|
bytes
|
A |
required |
get_acc
¶
Read the current accumulator value without modifying it. Use this when you need to snapshot the running total mid-stream and continue accumulating afterward.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
Examples:
set_acc
¶
Overwrite the accumulator with a new value. Useful for setting a bias before a new accumulation window, or for restoring a previously checkpointed value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
int
|
Input. |
required |
Examples:
destroy
¶
Release the underlying C resources immediately.
Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.
Idempotent: calling it again on an already-released object does
nothing. Every other method raises RuntimeError once it has run.
__enter__
¶
Enter a context manager, returning this object.
Lets a AccQ15 be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
AccQ15
|
This same object, not a copy. |
__exit__
¶
__exit__(
exc_type: object | None = ...,
exc: object | None = ...,
tb: object | None = ...,
) -> None
Exit a context manager, releasing the AccQ15.
Equivalent to calling destroy(). Returns None, so an exception
raised inside the with body propagates normally; this never
suppresses one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
object | None
|
Exception class, or None. Ignored. |
...
|
exc
|
object | None
|
Exception instance, or None. Ignored. |
...
|
tb
|
object | None
|
Traceback object, or None. Ignored. |
...
|
AccQ8
¶
Allocate and initialise an AccQ8 accumulator. The accumulator starts at the supplied initial value and accepts Q8 (int8_t) samples via step(), steps(), or madd(). The 32-bit internal register handles up to roughly 16 million max-magnitude samples before wrap — sufficient for all standard DSP block sizes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
acc
|
int
|
acc state variable. |
0
|
Examples:
reset
¶
Reset the accumulator to zero, mirroring the post-create state. Always resets to zero regardless of the original constructor value, so it is safe to call at the start of any new accumulation window.
Examples:
step
¶
Accumulate one Q8 sample into the running total. The sample is sign-extended to 32 bits before addition so negative samples correctly subtract from the accumulator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
Q8 input sample (int8_t, range |
required |
Examples:
steps
¶
Accumulate a contiguous block of Q8 samples. Equivalent to calling step() n times; the single loop is more amenable to auto-vectorisation than repeated method calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[int8]
|
Input. |
required |
Examples:
get
¶
dump
¶
Return the accumulated value and reset to zero.
Returns:
| Type | Description |
|---|---|
int
|
Accumulator value before the reset (int32_t). |
Examples:
madd
¶
Multiply-accumulate: acc += sum(a[i] * b[i]) for i in [0, len(a)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
First input array (int8_t). |
required |
b
|
NDArray[int8]
|
Second input array (int8_t), same length as a. |
required |
Examples:
state_bytes
¶
Size in bytes of this object's serialized state.
The exact length get_state returns and set_state requires. It
depends on how the object was constructed (state arrays are sized at
construction), so read it from the instance rather than assuming a
constant.
Raises RuntimeError if the AccQ8 has already been destroyed.
Returns:
| Type | Description |
|---|---|
int
|
Byte length of one serialized state blob. |
get_state
¶
Serialize this object's mutable state to bytes.
Captures exactly the state that evolves as the object runs, so a blob taken now and restored later resumes from this point. Construction parameters are not included: restore into an object built the same way.
The blob is opaque and always state_bytes() long. Its layout is an
implementation detail of the C core and is not a stable format across
builds.
Raises RuntimeError if the AccQ8 has already been destroyed.
Returns:
| Type | Description |
|---|---|
bytes
|
Opaque snapshot, |
set_state
¶
Restore mutable state from a get_state() blob.
Overwrites the live state in place; the object keeps the parameters it
was constructed with. Length is validated against state_bytes()
before the blob is handed to the C core, and the core may reject it as
well.
Raises TypeError if blob is not bytes, ValueError if its
length differs from state_bytes() or the core rejects it, and
RuntimeError if the AccQ8 has already been destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blob
|
bytes
|
A |
required |
get_acc
¶
set_acc
¶
Overwrite the accumulator with a new value. Useful for applying a bias before a new accumulation window, or for restoring a checkpointed accumulator state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
int
|
Input. |
required |
Examples:
destroy
¶
Release the underlying C resources immediately.
Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.
Idempotent: calling it again on an already-released object does
nothing. Every other method raises RuntimeError once it has run.
__enter__
¶
Enter a context manager, returning this object.
Lets a AccQ8 be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
AccQ8
|
This same object, not a copy. |
__exit__
¶
__exit__(
exc_type: object | None = ...,
exc: object | None = ...,
tb: object | None = ...,
) -> None
Exit a context manager, releasing the AccQ8.
Equivalent to calling destroy(). Returns None, so an exception
raised inside the with body propagates normally; this never
suppresses one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
object | None
|
Exception class, or None. Ignored. |
...
|
exc
|
object | None
|
Exception instance, or None. Ignored. |
...
|
tb
|
object | None
|
Traceback object, or None. Ignored. |
...
|
Q15 operations (1.15, int16)¶
add_q15
¶
Elementwise saturating two's complement add of two Q15 arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
First input array (int16_t). |
required |
b
|
NDArray[int16]
|
Second input array (int16_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int16]
|
Output. |
Examples:
sub_q15
¶
Elementwise saturating two's complement subtract of two Q15 arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
Minuend array (int16_t). |
required |
b
|
NDArray[int16]
|
Subtrahend array (int16_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int16]
|
Output. |
Examples:
mul_q15
¶
Elementwise Q15 multiply with round-half-up: out[i] = sat16((a[i]*b[i] + 16384) >> 15).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
First input array (int16_t). |
required |
b
|
NDArray[int16]
|
Second input array (int16_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int16]
|
Output. |
Examples:
dot_q15
¶
Inner product of two Q15 arrays. Returns the raw Q30 accumulation as int64_t. Shift right 15 to get a Q15 scalar.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
First input array (int16_t). |
required |
b
|
NDArray[int16]
|
Second input array (int16_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Raw Q30 accumulation (int64_t). |
Examples:
shl_q15
¶
Elementwise arithmetic left shift of a Q15 array with saturation. Equivalent to multiplying by 2^n in fixed-point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
Input array (int16_t). |
required |
n
|
int
|
Shift count (non-negative integer). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int16]
|
Output. |
Examples:
shr_q15
¶
Elementwise arithmetic right shift of a Q15 array with round-half-up. Equivalent to dividing by 2^n.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int16]
|
Input array (int16_t). |
required |
n
|
int
|
Shift count (non-negative integer). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int16]
|
Output. |
Examples:
Q8 operations (1.7, int8)¶
add_q8
¶
Elementwise saturating two's complement add of two Q8 arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
First input array (int8_t). |
required |
b
|
NDArray[int8]
|
Second input array (int8_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int8]
|
Output. |
Examples:
sub_q8
¶
Elementwise saturating two's complement subtract of two Q8 arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
Minuend array (int8_t). |
required |
b
|
NDArray[int8]
|
Subtrahend array (int8_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int8]
|
Output. |
Examples:
mul_q8
¶
Elementwise Q8 multiply with round-half-up: out[i] = sat8((a[i]*b[i] + 64) >> 7).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
First input array (int8_t). |
required |
b
|
NDArray[int8]
|
Second input array (int8_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int8]
|
Output. |
Examples:
dot_q8
¶
Inner product of two Q8 arrays. Returns the raw Q14 accumulation as int32_t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
First input array (int8_t). |
required |
b
|
NDArray[int8]
|
Second input array (int8_t), same length as a. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Raw Q14 accumulation (int32_t). |
Examples:
shl_q8
¶
Elementwise arithmetic left shift of a Q8 array with saturation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
Input array (int8_t). |
required |
n
|
int
|
Shift count (non-negative integer). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int8]
|
Output. |
Examples:
shr_q8
¶
Elementwise arithmetic right shift of a Q8 array with round-half-up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int8]
|
Input array (int8_t). |
required |
n
|
int
|
Shift count (non-negative integer). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int8]
|
Output. |
Examples:
64-bit saturating shifts¶
shl_i64
¶
Elementwise logical left shift of an int64_t array. No saturation (caller ensures no overflow).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int64]
|
Input array (int64_t). |
required |
n
|
int
|
Shift count (non-negative integer; >= 63 yields 0). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int64]
|
Output. |
Examples:
shr_i64
¶
Elementwise arithmetic right shift of an int64_t array with round-half-up. Useful for normalising dot_q15 Q30 results back to Q15.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
NDArray[int64]
|
Input array (int64_t). |
required |
n
|
int
|
Shift count (non-negative integer; >= 63 is clamped to 63). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int64]
|
Output. |
Examples:
Related pages¶
Guides — Getting Started with Fixed-Point Arithmetic Design — API taxonomy: the DSP building-block hierarchy and its naming axis