fixedpoint 1.0.1 Package Documentation

Release 1.0.1

The fixedpoint package offers several features that are widely used in DSP applications:

The fixedpoint package is unit-tested against MATLAB stimulus (with the fixed point toolbox), making the fixedpoint package a viable, accurate, and cost-free alternative to MATLAB.

Installation

The fixedpoint package is available on PyPI. You can install with pip:

pip install -U fixedpoint

The -U will install and/or update to the latest version.

Table of Contents

Fixed Point Basics

A FixedPoint object consists of:

Q Format

The fixedpoint module utilizes the “Q-format notation” to describe the word length and binary point position:

  • Qm.n represents a signed fixed point number with m integer bits and n fractional bits (that is, m + n total bits). The most significant bit has a negative weight.

  • UQm.n represents an unsigned fixed point number with m integer bits and n fractional bits (that is, m + n total bits). The most significant bit has a positive weight.

Furthermore:

  • Negative m or n is not allowed.

  • Total word length (m + n) must be positive.

  • Signed numbers must have m greater than 0, while unsigned numbers can have m equal to 0, as long as total word length is positive.

  • n can be 0 as long as total word length is positive.

Any deviation from the above specifications constitutes an invalid Q format.

Warning

Some notations reverse m and n. Some notations use A and U to represent signed and unsinged numbers, respectively. Some notations do not include the sign bit in the integer bits.

It is important to understand the Q format utilized by fixedpoint because there are various formats in use throughout the industry.

Representable Range

The Q format specifies the range and resolution of values that a fixed point number can represent.

Q format

Range

Resolution

Qm.n

\([-2^{m-1}, 2^{m-1})\)

\(2^{-n}\)

UQm.n

\([0, 2^m)\)

The FixedPoint Class

Examples are just a click away

Boxes like this link to example code.

Jump to Section

class fixedpoint.FixedPoint(init, /, signed=None, m=None, n=None, *, overflow='clamp', rounding='auto', overflow_alert='error', implicit_cast_alert='warning', mismatch_alert='warning', str_base=16)
Parameters
  • init (float or int or str or FixedPoint) – Initial value. This argument is required and positional, meaning it cannot be keyworded and must come first in the list of arguments.

  • signed (bool) – Signedness, part of the Q format specification. When left unspecified, sign() is used to deduce signedness. This argument can be keyworded.

  • m (int) – Number of integer bits, part of the Q format specification. When left unspecified, min_m() is used to deduce initial integer bit width, after which trim() is used after rounding to minimize integer bits. This argument can be keyworded.

  • n (int) – Number of fractional bits, part of the Q format specification. When left unspecified, min_n() is used to deduce fractional bit width. This argument can be keyworded.

  • overflow (str) –

    Specifies what shall happen when the value overflows its integer bit width. Valid options are:

    • 'clamp' (default when left unspecified)

    • 'wrap'

  • rounding (str) –

    Specifies how superfluous fractional bits are rounded away. Valid options are:

    • 'convergent' (default for signed when left unspecified)

    • 'nearest' (default for unsigned when left unspecified)

    • 'in'

    • 'out'

    • 'up'

    • 'down'

  • overflow_alert (str) –

    Specifies the notification scheme when overflow occurs. Valid options are:

    • 'error' (default when left unspecified)

    • 'warning'

    • 'ignore'

  • mismatch_alert (str) –

    Specifies the notification scheme when 2 FixedPoints with non-matching properties undergo arithmetic. Valid options are:

    • 'error'

    • 'warning' (default when left unspecified)

    • 'ignore'

  • implicit_cast_alert (str) –

    Specifies the notification scheme when implicit casting is performed and the resultant FixedPoint is not valued the same as the original number. Valid options are:

    • 'error'

    • 'warning' (default when left unspecified)

    • 'ignore'

  • str_base (int) –

    Casting a FixedPoint to a str generates a bit string in the base specified by str_base. Valid options are:

    • 16 (default when left unspecified)

    • 10

    • 8

    • 2

Raises
  • ValueError

    • if init is a str and any of signed, m, or n are not specified.

    • if more than m + n bits are present in init (when init is a str).

    • if an invalid Q format is specified.

  • TypeError – if init is not an int, float, str, or FixedPoint and cannot be cast to a float.

  • FixedPointOverflowError – if overflow_alert is 'error' and m is too small to represent init.

from_int(val)
Parameters

val (int) – Value to set the FixedPoint to.

Set the value of the FixedPoint from an integer value. Affects only integer bits (since integer require no fractional bits). Must fit into the Q format already designated by the object, otherwise overflow will occur.

Jump to Examples

from_float(val)
Parameters

val (float) – Value to set the FixedPoint to.

Set the value of the FixedPoint. Must fit into the Q format already designated by the object, otherwise rounding and/or overflow will occur.

Jump to Examples

from_string(val)
from_str(val)
Parameters

val (str) – Value to set the FixedPoint bits to.

Directly set the bits of the FixedPoint, using the Q format already designated by the object. May be a decimal, binary, octal, or hexadecimal string, the latter three of which require a '0b', '0o', or '0x' radix, respectively.

Jump to Examples

FixedPoint Properties

signed
Type

bool

Getter

True for signed, False for unsigned.

Setter

Set signedness.

Raises

Change signedness of number. Note that if the MSb is 0, the value of the number will not change. Overflow occurs if the MSb is 1.

m
Type

int

Getter

Number of integer bits in the FixedPoint number.

Setter

Set the number of integer bits in the FixedPoint number.

Raises

When the number of integer bits increases, sign extension occurs for signed numbers, and 0-padding occurs for unsigned numbers. When then number of integer bits decreases, overflow handling may occur (per the overflow property) if the FixedPoint value is too large for the new integer bit width.

n
Type

int

Getter

Number of fractional bits in the FixedPoint number.

Setter

Set the number of fractional bits in the FixedPoint number.

Raises

When the number of fractional bits increases, 0s are appended to the fixed point number. When the number of fractional bits decreases, rounding may occur (per the rounding property), which in turn may cause overflow (per the overflow property) if the integer portion of the rounded result is too large to fit within the current integer bit width.

str_base
Type

int

Getter

Base of the string generated by str.

Setter

Set the base of the string generated by str.

Using the builtin python str function on a FixedPoint casts the object to a string. The string is the bits of the FixedPoint number in the base specified by str_base, but without the radix. Must be one of:

  • 16

  • 10

  • 8

  • 2

Jump to Examples

overflow
Type

str

Getter

The current overflow scheme.

Setter

Set the overflow scheme.

Overflow occurs when the number of bits required to represent a value exceeds the number of integer bits available (m). The overflow property of a FixedPoint specifies how to handle overflow. Must be one of:

  • 'clamp'

  • 'wrap'

Jump to Examples

rounding
Type

str

Getter

The current rounding scheme.

Setter

Set the rounding scheme.

Rounding occurs when fractional bits must be removed from the object. Some rounding schemes can cause overflow in certain circumstances. Must be one of:

  • 'convergent'

  • 'nearest'

  • 'in'

  • 'out'

  • 'up'

  • 'down'

Jump to Examples

overflow_alert
Type

str

Getter

The current overflow_alert scheme.

Setter

Set the overflow_alert scheme.

When overflow occurs, the overflow_alert property indicates how you are notified. Must be one of:

  • 'error'

  • 'warning'

  • 'ignore'

Jump to Examples

mismatch_alert
Type

str

Getter

The current mismatch_alert scheme.

Setter

Set the mismatch_alert scheme.

When 2 FixedPoints interact to create another FixedPoint, the properties assigned to the new object must be resolved from the 2 original objects. Whenever properties between these 2 objects do not match, the mismatch_alert property indicates how you are notified. Must be one of:

  • 'warning'

  • 'error'

  • 'ignore'

Jump to Examples

implicit_cast_alert
Type

str

Getter

The current implicit_cast_alert scheme.

Setter

Set the implicit_cast_alert scheme.

Some operations allow a FixedPoint to interact with another object that is not a FixedPoint. Typically, the other object will need to be cast to a FixedPoint, and is done so internally in the class method. If error exists after the cast to FixedPoint, the implicit_cast_alert property indicates how you are notified. Must be one of:

  • 'warning'

  • 'error'

  • 'ignore'

Jump to Examples

bits
Type

FixedPointBits

Getter

Bits of the fixed point number.

This is the read-only bits of the FixedPoint, stored as an integer.

Indexing, slicing, and mapping is available with the FixedPointBits class.

bitmask
Type

int

Getter

Bitmask of the FixedPoint number.

Integer bitmask, equivalent to \(2^{m + n} - 1\).

clamped
Type

bool

Getter

True if the value of the FixedPoint number is equal to it minimum or maximum value. False otherwise.

qformat
Type

str

Getter

Q format of the FixedPoint number.

The string takes the form UQm.n, where:

  • U is only present for unsigned numbers

  • m is the number of integer bits

  • n is the number of fractional bits

Arithmetic Operators

__add__(augend)
__iadd__(augned)
__radd__(addend)

Note

These are the + and += operators.

Parameters
Returns

Sum of addend and augend

Return type

FixedPoint

Raises

Note

\(\it{sum} = \it{addend} + \it{augend}\)

Addition using the + and += operators are full precision; bit growth will occur:

If both augend or addend are unsigned, the result is unsigned, otherwise the result will be signed.

Jump to Examples

__sub__(subtrahend)
__isub__(subtrahend)
__rsub__(minuend)

Note

These are the - and -= operators.

Parameters
Returns

Difference of minuend and subtrahend

Return type

FixedPoint

Raises

Note

\(\it{difference} = \it{minuend} - \it{subtrahend}\)

Subtraction using the - and -= operators are full precision; bit growth will occur.

If both minuend or subtrahend are unsigned, the result is unsigned, otherwise the result will be signed.

Overflow can occur for unsigned subtraction.

Jump to Examples

__mul__(multiplier)
__imul__(multiplier)
__rmul__(multiplicand)

Note

These are the * and *= operators.

Parameters
Returns

Product of multiplicand and multiplier

Return type

FixedPoint

Raises

Note

\(\it{product} = \it{multiplicand} \times \it{multiplier}\)

Multiplication using the * and *= operators are full precision; bit growth will occur.

If both multiplicand or multiplier are unsigned, the result is unsigned, otherwise the result will be signed.

Jump to Examples

__pow__(exponent)
__ipow__(exponent)

Note

These are the ** and **= operators.

Parameters

exponent (int) – The exponent to the FixedPoint base. Must be positive.

Returns

Result of the base raised to the exponent power.

Return type

FixedPoint

Note

\(\it{result} = \it{base}^{\it{exponent}}\)

Exponentiation using the ** and **= operators are full precision; bit growth will occur.

The result has the same signedness as the base.

Only positive integers are supported as the exponent.

Jump to Examples

Comparison Operators

__lt__(other)
__le__(other)
__gt__(other)
__ge__(other)
__eq__(other)
__ne__(other)

Note

These are the <, <=, >, >=, == and != operators.

Parameters

other (FixedPoint or int or float) – Numeric object to compare to

Returns

True if the comparison is true, False otherwise

Return type

bool

__cmp__(other)
Parameters

other (FixedPoint or int or float) – Numeric object to compare to

Returns

  • a negative number if the object is < other

  • 0 if the object == other

  • a positive number if the object is > other

Return type

int

Generic comparison object. Not used for comparisons in python 3 but used internally by all other comparisons.

Bitwise Operators

__lshift__(nbits)
__ilshift__(nbits)

Note

These are the << and <<= operators.

Parameters

nbits (int) – Number of bits to shift left.

Return type

FixedPoint

Bit shifting does not change the FixedPoint’s Q format. The nbits leftmost bits are discarded.

To keep bits after shifting, multiply the object by \(2^{nbits}\) instead of using the << or <<= operator.

If nbits < 0, bits are shifted right using >> or >>= by abs(nbits) instead.

Jump to Examples

__rshift__(nbits)
__irshift__(nbits)

Note

These are the >> and >>= operators.

Parameters

nbits (int) – Number of bits to shift right.

Returns

Original FixedPoint with bits shifted right.

Return type

FixedPoint

Bit shifting does not change the FixedPoint’s Q format. The nbits rightmost bits are discarded.

To keep bits after shifting, multiply the object by \(2^{-nbits}\) instead of using the >> or >>= operator.

For signed numbers, sign extension occurs.

If nbits < 0, bits are shifted right using << or <<= by abs(nbits) instead.

Jump to Examples

__and__(other)
__iand__(other)
__rand__(other)

Note

These are the & and &= operators.

Parameters

other (int or FixedPoint) – Object to bitwise AND with

Returns

Original object’s bits bitwise ANDed with other’s bits.

Return type

FixedPoint

When ANDing 2 FixedPoints, the binary point is not aligned.

After ANDing, the result is masked with the leftmost FixedPoint.bitmask and assigned to the bits of the return value.

Jump to Examples

__or__(other)
__ior__(other)
__ror__(other)

Note

These are the | and |= operators.

Parameters

other (int or FixedPoint) – Object to bitwise OR with

Returns

Original object’s bits bitwise ORed with other’s bits.

Return type

FixedPoint

When ORing 2 FixedPoints, the binary point is not aligned.

After ORing, the result is masked with the leftmost FixedPoint.bitmask and assigned to the bits of the return value.

Jump to Examples

__xor__(other)
__ixor__(other)
__rxor__(other)

Note

These are the ^ and ^= operators.

Parameters

other (int or FixedPoint) – Object to bitwise XOR with

Returns

Original object’s bits bitwise XORed with other’s bits.

Return type

FixedPoint

When XORing 2 FixedPoints, the binary point is not aligned.

After XORing, the result is masked with the leftmost FixedPoint.bitmask and assigned to the bits of the return value.

Jump to Examples

Unary Operators

__invert__()

Note

This is the unary ~ operator.

Returns

Copy of original object with bits inverted.

Return type

FixedPoint

Jump to Examples

__pos__()

Note

This is the unary + operator.

Returns

Copy of original object.

Return type

FixedPoint

__neg__()

Note

This is the unary - operator.

Returns

Negated copy of original object negated.

Return type

FixedPoint

Raises

In an attempt to minimize user error, unsigned numbers cannot be negated. The idea is that you should be doing this very intentionally.

Jump to Examples

Built-in Function Support

__abs__()

Note

This is the built-in abs() function.

Returns

Absolute value.

Return type

FixedPoint

Raises

FixedPointOverflowError – if the absolute value of a negative-valued number is larger than the Q format allows (raised only if overflow_alert is 'error').

Signedness does not change.

Jump to Examples

__int__()

Note

This is the built-in int function.

Returns

Only the integer bits of the FixedPoint number.

Return type

int

Fractional bits are ignored, which is the same as rounding down.

__float__()

Note

This is the built-in float function.

Returns

Floating point cast of the FixedPoint number.

Return type

float

When casting to a float would result in an OverflowError, float('inf') or float('-inf') is returned instead.

Warning

A typical Python float follows IEEE 754 double-precision format, which means there’s 52 mantissa bits and a sign bit (you can verify this by examining sys.float_info). Thus for FixedPoint word lengths beyond 52 bits, the float cast may lose precision or resolution.

__bool__()

Note

This is the built-in bool function.

Returns

False if FixedPoint.bits are non-zero, True otherwise.

Return type

bool

__index__()

Note

This is the built-in hex(), oct(), and bin() functions.

Returns

Bits of the FixedPoint number.

Return type

int

Calling hex(), oct(), or bin() on a FixedPoint generates a str with the FixedPoint.bits represented as a hexadecimal, octal, or binary string. The radix prepends the bits, which do not contain any left-padded zeros.

__str__()

Note

This is the built-in str function.

Returns

Bits of the FixedPoint number, left padded to the number of bits in the number, in the base specified by the str_base property.

Return type

str

Calling str() will generate a hexadecimal, octal, or binary string (according to the str_base property setting) without the radix, and 0-padded to the actual bit width of the FixedPoint number. Decimal strings are not 0-padded.

This string represents the bits of the number, thus will always be non-negative.

Signedness does not change.

Jump to Examples

__format__()

Note

This is the built-in str.format() and format() function, and also applies to f-strings.

Returns

Formatted string, various formats available.

Return type

str

A FixedPoint can be formatted as a str, float, or int would, depending on the format string syntax.

Standard Format Specifier Parsing Summary
format_spec
type

Formatted Type

Formatted Value
(given x = FixedPoint(...))

's'

str

str(x)
(depends on x.str_base)

'q'

x.qformat

'b'
(binary)

int

x.bits

'd'
(decimal)
'o'
(octal)
'x'
(lowercase
hexadecimal)
'X'
(uppercase
hexadecimal)

'...m' 1

x.bits['int']
(integer bits only)

'...n' 1

x.bits['frac']
(fractional bits only)

'e'

float

float(x)

'E'

'f'

'F'

'g'

'G'

'%'

1 Append to the specifier of another formatted int. E.g., 'bn' would format the fractional bits of x in binary.

Jump to Examples

__len__()

Note

This is the built-in len() function..

Returns

Number of bits in the FixedPoint.

Return type

int

__repr__()

Note

This is the built-in repr() function, which is also the output shown when a FixedPoint is not assigned to a variable.

Returns

Python executable code; a str representation of the object.

Return type

str

This generates a code string that will exactly reproduce the FixedPoint’s value and properties.

Bit Resizing Methods

resize(m, n, /, rounding=None, overflow=None, alert=None)
Parameters
  • m (int) – Number of integer bits to resize to.

  • n (int) – Number of fractional bits to resize to

  • rounding (str) – Temporary rounding scheme to use. Can be keyworded.

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Raises

FixedPointOverflowError – if resizing causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error').

Fractional bits are resized first, them integer bits. Bit sizes can grow or shrink from their current value.

Rounding, overflow handling, and overflow alert notification severity can be temporarily modified within the scope of this method. I.e., specifying the rounding, overflow, or alert arguments will only take effect within this method; it will not permanently change the property settings of the object. If left unspecified, the current property setting is used.

trim(ints=None, fracs=None)
Parameters
  • ints (bool) – Set to True to trim off superfluous integer bits

  • fracs (bool) – Set to True to trim off superfluous fractional bits

Trims off excess bits, including:

  • up to n trailing 0s

  • for unsigned numbers:

    • up to m leading 0s

  • for signed numbers:

    • up to m - 1 leading 0s for positive numbers, leaving one leading 0 in front of the first 1 encountered

    • up to m - 1 leading 1s, for negative numbers, leaving one leading 1 in front of the first 0 encountered

Resultant Q format is always valid. For the FixedPoint value of 0, resulting Q format is [U]Q1.0.

Opt to trim off only fractional bits or only integer bits by setting fracs or ints, respectively, to True. When left unspecified, both integer and fractional bits are trimmed.

Jump to Examples

Rounding Methods

__round__(n)

Note

This is the built-in round() function.

Parameters

n (int) – Number of bits remaining after round

Returns

A copy of the FixedPoint rounded to n bits.

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if the overflow_alert property setting is 'error').

Rounds a copy of the FixedPoint using the rounding scheme specified by the rounding property setting.

Refer to FixedPoint.resize() for more details.

Jump to Examples

__floor__()

Note

This is the built-in math.floor() function. It does not modify the object given to it, but creates a copy and operates on it instead.

Return type

FixedPoint

Rounds to the integer closest to \(-\infty\), but does not modify the fractional bit width.

Jump to Examples

__ceil__()

Note

This is the built-in math.ceil() function. It does not modify the object given to it, but creates a copy and operates on it instead.

Return type

FixedPoint

RaisesFixedPointOverflowError

if the integer value of the FixedPoint is already at its maximum possible value (raised only if overflow_alert is 'error')

Rounds to the integer closest to \(+\infty\), leaving 0 fractional bits. For values other than 0, this requires m to be non-zero.

Jump to Examples

__trunc__()

Note

This is the built-in math.trunc() function. It does not modify the object given to it, but creates a copy and operates on it instead.

Return type

FixedPoint

Rounds to the integer closest to \(-\infty\), leaving 0 fractional bits. If m is 0, it is changed to 1, otherwise m is not modified.

Jump to Examples

round(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Rounds the FixedPoint using the rounding scheme specified by the rounding property setting.

convergent(n)
round_convergent(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Rounds to n fractional bits, biased toward the nearest value with ties rounding to the nearest even value.

round_nearest(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Rounds the FixedPoint to n fractional bits, biased toward the nearest value with ties rounding to \(+\infty\).

round_in(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Rounds the FixedPoint to n fractional bits toward 0.

round_out(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Rounds the FixedPoint to n fractional bits, biased toward the nearest value with ties rounding away from 0.

round_down(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Rounds the FixedPoint to n fractional bits toward \(-\infty\).

round_up(n)
Parameters

n (int) – Number of fractional bits remaining after rounding

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Rounds the FixedPoint to n fractional bits toward \(+\infty\).

keep_msbs(m, n, /, rounding=None, overflow=None, alert=None)
Parameters
  • m (int) – Number of integer bits in the result

  • n (int) – Number of fractional bits in the result

  • rounding (str) – Temporary rounding scheme to use. Can be keyworded.

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Rounds away LSb(s), leaving m + n bit(s), using the rounding scheme specified, then interprets the result with m integer bits and n fractional bits.

The rounding, overflow handling, and overflow alert notification schemes can be temporarily modified within the scope of this method. I.e., specifying the rounding, overflow, or alert arguments will only take effect within this method; it will not permanently change the property settings of the object. The current property setting for any of these unspecified arguments is used.

While other rounding functions cannot round beyond the fractional bits in a FixedPoint, keep_msbs() will keep an arbitrary number of the FixedPoint’s most significant bits, regardless of its current Q format. The resulting Q format must be valid.

Overflow Handling

clamp(m, /, alert=None)
Parameters
  • m (int) – Number of integer bits remaining after clamping

  • alart (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Raises

FixedPointOverflowError – if new integer bit width is too small to represent the FixedPoint object value (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Reduces the number of integer bits in the FixedPoint to m, clamping to the minimum or maximum value on overflow.

The overflow alert notification scheme can be temporarily modified within the scope of the method by using the alert argument. When left unspecified, the overflow_alert property setting is used.

wrap(m, /, alert=None)
Parameters
  • m (int) – Number of integer bits remaining after wrapping

  • alart (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Raises

FixedPointOverflowError – if new integer bit width is too small to represent the FixedPoint object value (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Reduces the number of integer bits in the FixedPoint to m, masking away the removed integer bits.

The overflow alert notification scheme can be temporarily modified within the scope of the method by using the alert argument. When left unspecified, the overflow_alert property setting is used.

keep_lsbs(m, n, /, overflow=None, alert=None)
Parameters
  • m (int) – Number of integer bits in the result

  • n (int) – Number of fractional bits in the result

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Raises

FixedPointOverflowError – if new m + n bits is too small to represent the FixedPoint value (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Removes MSb(s), leaving m + n bit(s), using the overflow scheme specified, then interprets the result with m integer bits and n fractional bits.

The overflow handling and overflow alert notification schemes can be temporarily modified within the scope of this method. I.e., specifying the overflow or alert arguments will only take effect within this method; it will not permanently change the property settings of the object. The current property setting for any of these unspecified arguments is used.

While other overflow handling functions cannot remove MSbs beyond their integer bits in a FixedPoint, keep_lsbs() will keep an arbitrary number of the FixedPoint’s least significant bits, regardless of its current Q format. The resulting Q format must be valid.

Context Management

__enter__()
__exit__(exc_type, *args)
__call__(*, safe_retain=False, **props)

Note

This is the built-in with statement in conjunction with the () operator.

Parameters
  • safe_retain (bool) – Set to True to retain the changes made within the context as long as no exceptions were raised. Set to False (or leave unspecified) if the the changes made within the context are to be undone when the context exits.

  • props

    Any keyword-able argument from the FixedPoint constructor, including:

    • signed (bool)

    • m (int)

    • n (int)

    • overflow (str)

    • rounding (str)

    • overflow_alert (str)

    • mismatch_alert (str)

    • implicit_cast_alert (str)

    • str_base (int)

Raises

While the __call__ method is not typically associated with the context manager, the FixedPoint class uses this method to assign attributes temporarily (or permanently, with appropriate use of the safe_retain keyword) to the FixedPoint called, within the context of the with statement.

Using the __call__ method is optional when safe_retain does not need to be True.

Jump to Examples

static enable_logging()

Enables logging to fixedpoint.log, located in the root directory of the fixedpoint module.

On initial import, logging is disabled.

Any time this method is called, fixedpoint.log is erased.

static disable_logging()

Disables logging to fixedpoint.log.

classmethod sign(val)
Parameters

val (FixedPoint or int or float) – Value from which to discern the sign.

Returns

  • -1 if val < 0

  • +1 if val > 0

  • 0 if val == 0

Return type

int

Determine the sign of a number.

classmethod min_m(val, /, signed=None)
Parameters
  • val (int or float) – Value to analyze

  • signed (bool) – True if signed, False if unsigned

Returns

Minimum value for FixedPoint.m for which val can be represented without overflow.

Return type

int

Calculate the minimum value for FixedPoint.m for which va can be represented without overflow. If signed is not specified, it is deduced from the value of val. When val < 0, signed is ignored.

Worst case rounding is assumed (e.g., min_m(3.25) returns 3, in case 3.25 needs to be rounded up to 4).

classmethod min_n(val)
Parameters

val (float) – Value to analyze

Returns

Minimum value for FixedPoint.n for which val can be represented exactly.

Return type

int

The FixedPointBits Class

Examples are just a click away

Boxes like this link to example code.

class fixedpoint.FixedPointBits

FixedPointBits inherits from int and adds item access. You should not need to instantiate this, but this allows for the FixedPoint.bits attribute to be indexed, sliced, and mapped.

s
Type

bool

Value

True for signed, False for unsigned.

m
Type

int

Value

Number of integer bits.

n
Type

int

Value

Number of fractional bits.

__getitem__(key)

Note

This is the built-in square bracket [] operator.

Parameters

key (int or slice or str) – Bit index, slice, or mapping

Return type

int or str

Raises
  • KeyError – Unsupported mapping string

  • IndexError – Invalid slice step or index out of range

The square brackets allow access to one or more bits at a time. No matter the access scheme (indexing, slicing, or mapping, described below), the return value is always shifted to be no more than N bits, where N is the number of bits accessed. E.g., accessing 3 bits will return an integer in the range [0, 23), regardless of where the are located in the FixedPointBits.

Indexing

When key is an int, a single bit is accessed in FixedPoint.bits. Index 0 is the LSb and index \(m + n - 1\) is the MSb.

Jump to Examples

Slicing

When key is a slice (either an explicit slice object, or generated by using one or more :s), one or more bits can be accessed. With bits as FixedPointBits and integers A, B, and C such that A > B:

  • bits[A:B:C] returns bits A down to B (inclusive) with index 0 being the LSb and \(m + n - 1\) being the MSb. C can be omitted, but must be -1 if specified.

  • bits[B:A:C] returns bits A up to B (inclusive) with index 0 being the MSb and \(m + n - 1\) being the LSb. C can be omitted, but must be 1 if specified.

  • bits[A:A:C] with C == -1 returns bit A within index 0 being the LSb and \(m + n - 1\) being the MSb.

  • bits[B:B:C] with C == 1 returns bit B within index 0 being the MSb and \(m + n - 1\) being the LSb.

Any slicing format not specified above treats the FixedPointBits as a binary digit str (indexed from 0 to \(m + n - 1\)).

Jump to Examples

Mapping

Common bit slices are mapped to string keywords:

Key String

Bit Slice

Assumptions

'm'

integer bits only

FixedPointBits.m > 0

'int'

'n'

fractional bits only

FixedPointBits.n > 0

'frac'

's'

most significant bit

'sign'

'msb'

'lsb'

least significant bit

If the mapping is accessed and the assumption(s) for that mapping are not satisfied, a KeyError is raised.

When the key string is UPPERCASED, the return value is a str of binary bits.

Jump to Examples

The PropertyResolver Class

Examples are just a click away

Boxes like this link to example code.

class fixedpoint.properties.PropertyResolver

Resolves properties between two FixedPoints.

This is used internally by the FixedPoint class for property resolution. You should not need to instantiate this class, but it is documented here to show how properties are resolved.

mismatch_alert(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose mismatch_alert properties are to be resolved.

Returns

Resolved mismatch_alert property.

Return type

str

Raises

MismatchError – if mismatch_alert properties of all args do not match, and any argsmismatch_alert property setting is 'error'.

When all args have equivalent mismatch_alert properties, that value is returned. Otherwise, the priority of resolution order is:

  1. 'warning'

  2. 'error'

  3. 'ignore'

If there are mismatches in the mismatch_alert properties, then an alert is issued according to the highest priority mismatch_alert setting in args.

overflow(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose overflow properties are to be resolved.

Returns

Resolved overflow property.

Return type

str

Raises

MismatchError – if overflow or mismatch_alert properties of all args do not match, and any argsmismatch_alert property setting is 'error'.

When all args have equivalent overflow properties, that value is returned. Otherwise, the priority of resolution order is:

  1. 'clamp'

  2. 'wrap'

If there are mismatches in the mismatch_alert properties, then an alert is issued according to the highest priority mismatch_alert setting in args.

rounding(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose rounding properties are to be resolved.

Returns

Resolved rounding property.

Return type

str

Raises

MismatchError – if rounding or mismatch_alert properties of all args do not match, and any argsmismatch_alert property setting is 'error'.

When all args have equivalent rounding properties, that value is returned. Otherwise, the priority of resolution order is:

  1. 'convergent' (if any args are signed, otherwise 'nearest')

  2. 'nearest' (if no args are signed, otherwise 'convergent')

  3. 'down'

  4. 'in'

  5. 'out'

  6. 'up'

If there are mismatches in the mismatch_alert properties, then an alert is issued according to the highest priority mismatch_alert setting in args.

overflow_alert(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose overflow_alert properties are to be resolved.

Returns

Resolved overflow_alert property.

Return type

str

Raises

MismatchError – if mismatch_alert or overflow_alert properties of all args do not match.

When all args have equivalent overflow_alert properties, that value is returned. Otherwise, the priority of resolution order is:

  1. 'error'

  2. 'warning'

  3. 'ignore'

If there are mismatches in the overflow_alert properties, then an alert is issued according to the highest priority mismatch_alert setting in args.

implicit_cast_alert(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose implicit_cast_alert properties are to be resolved.

Returns

Resolved implicit_cast_alert property.

Return type

str

Raises

MismatchError – if mismatch_alert or implicit_cast_alert properties of all args do not match.

When all args have equivalent implicit_cast_alert properties, that value is returned. Otherwise, the priority of resolution order is:

  1. 'warning'

  2. 'error'

  3. 'ignore'

If there are mismatches in the implicit_cast_alert properties, then an alert is issued according to the highest priority mismatch_alert setting in args.

str_base(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose str_base properties are to be resolved.

Returns

Resolved str_base property.

Return type

int

When all args have equivalent str_base properties, that str_base is returned. Otherwise the resolution is 16.

Note

str_base mismatches do not raise MismatchErrors.

all(*args)
Parameters

args (FixedPoint) – An variable number of FixedPoints whose property settings are to be resolved.

Returns

dict of resolved properties.

Return type

dict[str, str]

Raises

MismatchError – if any properties are not equivalent for all args and any argsmismatch_alert property setting is 'error'.

Resolves all properties for each FixedPoint in args.

Return value is a dict, with the format 'property name': 'property setting'. This can be used directly in the FixedPoint constructor as its property keyword arguments.

A mismatch alert is issued for each property mismatch.

Property Resolution Order

The order in which properties are resolved (and thus the order in which alerts may be issued) is:

Functions

The fixedpoint module functions provide the same functionality as the FixedPoint methods of the same name, but make a copy of the FixedPoint object and operate on it, instead of modifying the object itself.

Examples are just a click away

Boxes like this link to example code.

fixedpoint.resize(fp, m, n, /, rounding=None, overflow=None, alert=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • m (int) – Number of integer bits to resize to.

  • n (int) – Number of fractional bits to resize to

  • rounding (str) – Temporary rounding scheme to use. Can be keyworded.

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Return type

FixedPoint

Raises

FixedPointOverflowError – if resizing causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error').

Refer to FixedPoint.resize() for more details.

Jump to Examples

fixedpoint.trim(fp, /, ints=None, fracs=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • ints (bool) – Set to True to trim off superfluous integer bits

  • fracs (bool) – Set to True to trim off superfluous fractional bits

Return type

FixedPoint

Refer to FixedPoint.trim() for more details.

Jump to Examples

fixedpoint.convergent(fp, n, /)
fixedpoint.round_convergent(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Refer to FixedPoint.convergent() for more details.

fixedpoint.round_nearest(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Refer to FixedPoint.round_nearest() for more details.

fixedpoint.round_in(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Refer to FixedPoint.round_in() for more details.

fixedpoint.round_out(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Refer to FixedPoint.round_out() for more details.

fixedpoint.round_up(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if overflow_alert is 'error')

Refer to FixedPoint.round_up() for more details.

fixedpoint.round_down(fp, n, /)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • n (int) – Number of fractional bits remaining after rounding

Return type

FixedPoint

Refer to FixedPoint.round_down() for more details.

fixedpoint.keep_msbs(fp, m, n, /, rounding=None, overflow=None, alert=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • m (int) – Number of integer bits in the result

  • n (int) – Number of fractional bits in the result

  • rounding (str) – Temporary rounding scheme to use. Can be keyworded.

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme touse. Can be keyworded.

Return type

FixedPoint

Raises

FixedPointOverflowError – if rounding causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Refer to FixedPoint.keep_msbs() for more details.

fixedpoint.clamp(fp, m, /, alert=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • m (int) – Number of integer bits remaining after clamping

  • alart (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Return type

FixedPoint

Raises

FixedPointOverflowError – if reducing integer bit width causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Refer to FixedPoint.clamp() for more details.

fixedpoint.wrap(fp, m, /, alert=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • m (int) – Number of integer bits remaining after wrapping

  • alart (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Return type

FixedPoint

Raises

FixedPointOverflowError – if reducing integer bit width causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Refer to FixedPoint.wrap() for more details.

fixedpoint.keep_lsbs(fp, m, n, /, overflow=None, alert=None)
Parameters
  • fp (FixedPoint) – Object to copy and operate on

  • m (int) – Number of integer bits in the result

  • n (int) – Number of fractional bits in the result

  • overflow (str) – Temporary overflow scheme to use. Can be keyworded.

  • alert (str) – Temporary overflow_alert scheme to use. Can be keyworded.

Return type

FixedPoint

Raises

FixedPointOverflowError – if reducing integer bit width causes overflow (raised only if alert - or overflow_alert if alert is not specified - is 'error')

Refer to FixedPoint.keep_lsbs() for more details.

Exceptions

exception fixedpoint.FixedPointError

Base class for other fixedpoint exceptions.

exception fixedpoint.FixedPointOverflowError

Signals that overflow has occurred. Raised only when overflow_alert is 'error'.

Inherits from FixedPointError and OverflowError.

exception fixedpoint.MismatchError

Signals that the properties of 2 FixedPoints do not match. Raised only when mismatch_alert is 'error'.

Inherits from FixedPointError.

exception fixedpoint.ImplicitCastError

Signals that an object required implicit casting to a FixedPoint, and the cast was not exact. Raised only when implicit_cast_alert is 'error'.

Inherits from FixedPointError and FloatingPointError.

Initialization

Initializing from a float

When initializing a FixedPoint from a float, signed, m, and n are all optional. Thus the following object instantiations are all valid:

>>> from fixedpoint import FixedPoint
>>> from math import pi
>>> a = FixedPoint(pi) # No signed, m, or n argument
>>> float(a), a.qformat
(3.141592653589793, 'UQ2.48')

>>> b = FixedPoint(pi, True) # No m or n argument
>>> float(b), b.qformat
(3.141592653589793, 'Q3.48')

>>> c = FixedPoint(2**-80, m=10, n=80) # No signed argument
>>> float(c), c.qformat
(8.271806125530277e-25, 'UQ10.80')

Warning

Python’s float type is typically implemented with 64-bit doubles, specified by IEEE 754. This means that there’s only 53 significant binary bits in a float (you can verify this by examining sys.float_info), and thus precision and resolution may be compromised in certain circumstances. See The Python Tutorial on the Issues and Limitations of Floating Point Arithmetic.

>>> print(format(pi, '.60f'))
3.141592653589793115997963468544185161590576171875000000000000
>>> a = FixedPoint(pi, n=60, str_base=2)
>>> print(str(a))
11001001000011111101101010100010001000010110100011000000000000

In the example above, \(\pi\) is only representable by 52 floating point mantissa bits. Thus extending the decimal representation out to 60 places, the bits eventually stabilize to 0 (even though \(\pi\) is irrational and never stabilizes). While the FixedPoint representation of the float is accurate, this method of representing \(\pi\) suffers from inaccuracy.

Initializing from an int

When initializing a FixedPoint from an int, signed, m, and n are all optional. When n is left unspecified, it is guaranteed to be 0 (since integers never require fractional bits). When m is left unspecified, FixedPoint.min_m() is used to deduce the number of integer bits needed to represent init, and after rounding occurs, trim() is used to remove superfluous leading 0s or sign-extended 1s.

>>> a = FixedPoint(-2**1000, n=42) # No signed or m argument
>>> a.qformat
'Q1001.42'

>>> b = FixedPoint(14, True, 10) # No n argument
>>> b.qformat, float(b)
('Q10.0', 14.0)

>>> c = FixedPoint(0, 0, 19, 88) # Signed, m, and n arguments are present
>>> str(c)
'000000000000000000000000000'

Tip

Python’s int type has unlimited precision, meaning it can be as large as you need it to be! In fact, the FixedPoint bits are stored internally as an int.

Initializing from a str

When initializing a FixedPoint from a str, signed, m, and n are required:

>>> a = FixedPoint('1') # no Q format
Traceback (most recent call last):
    ...
ValueError: String literal initialization Q format must be fully constrained.

The string is converted to an int and this value is stored internally as the FixedPoint bits. This means that leading 0s are ignored and not included in the total number of bits:

>>> a = FixedPoint('0x00000000000000001', signed=0, m=1, n=0) # Leading 0s
>>> a.bits
1

Rounding and overflow handling are not performed. If the bits of the string exceed the specified Q format, a ValueError is raised.

>>> a = FixedPoint('0xFF', 0, 1, 1) # A whole bunch of extra bits
Traceback (most recent call last):
   ...
ValueError: Superfluous bits detected in string literal '0xFF' for UQ1.1 format.

int(init, 0) is used to internally convert the str to int, thus the 0b, 0o, or 0x radix is required for binary, octal, or hexadecimal strings, respectively. If the radix is not present, it is considered a decimal number. This means if you have an integer that represents some known/desired Q format, you can simply call bin(), oct(), str or hex() to convert it to a str, then to a FixedPoint. This method of initialization may be super useful in generating random stimulus:

>>> import random
>>> random.seed(42)
>>> signed, m, n = 1, 12, 13
>>> bits = random.getrandbits(m + n)
>>> x = FixedPoint(hex(bits), signed, m, n)
>>> float(x), x.qformat
(-1476.9078369140625, 'Q12.13')

Warning

Converting a negative integer to a string is not allowed; the bits of interest should be masked before converting to a string.

>>> x = FixedPoint('-1', signed=1, m=2, n=0)
Traceback (most recent call last):
   ...
ValueError: Superfluous bits detected in string literal '-1' for Q2.0 format.

>>> m, n = 2, 0
>>> mask = 2**(m + n) - 1
>>> x = FixedPoint(str(-1 & mask), 1, m, n)
>>> float(x), x.qformat
(-1.0, 'Q2.0')

Initializing from another FixedPoint

When initializing a FixedPoint from another FixedPoint, only init is required; all other arguments are ignored.

>>> x = FixedPoint(2**-5)
>>> x.qformat
'UQ0.5'

>>> y = FixedPoint(x, n=492) # n is ignored
>>> y.qformat
'UQ0.5'

Initializing from other types

When init is not a(n) float, int, str, or FixedPoint, the object is cast to a float.

>>> from decimal import Decimal
>>> x = Decimal(1) / Decimal(10)
>>> y = FixedPoint(x)
>>> z = FixedPoint(0.1)
>>> y == z
True

If this fails, a TypeError is raised.

>>> file = open('some_file', 'a')
>>> FixedPoint(file)
Traceback (most recent call last):
    ...
TypeError: Unsupported type <class '_io.TextIOWrapper'>; cannot convert to float.

Initializers

When the Q format of a FixedPoint should stay the same, but a different value is needed, it is quicker to use one of the following initializers than generating a new FixedPoint object:

It’s quicker because the Q format and properties need not be validated.

from timeit import timeit

test_constructor = {
    'stmt': 'FixedPoint(hex(random.getrandbits(100)), 1, 42, 58)',
    'setup': 'from fixedpoint import FixedPoint; import random',
    'number': 100000,
}

test_initializer = {
    'stmt': 'x.from_string(hex(random.getrandbits(100)))',
    'setup': 'import fixedpoint, random; x = fixedpoint.FixedPoint(0, 1, 42, 58)',
    'number': 100000,
}

print("Constructor:", timeit(**test_constructor))
print("Initializer:", timeit(**test_initializer))

The code block above tests the difference between creating 100,000 FixedPoint instances and 100,000 initializer calls. There is a significant time improvement by using the initializer from_string() instead of the FixedPoint constructor:

Constructor: 1.6910291
Initializer: 0.18432009999999988

The results are similar with from_int():

Constructor: 1.8801342
Initializer: 0.4326542

and from_float() (using random.random() with 50 fractional bits):

Constructor: 2.4381994
Initializer: 0.7997059000000002

One example is when stimulus is being created that uses a single FixedPoint at a time, or when the processing of that FixedPoint does not change its properties. Create a single instance, and then just use the initializer in each loop iteration:

from fixedpoint import FixedPoint
import random

# Q1.24
qformat = {'signed': True, 'm': 1, 'n': 24}

# A single instance
x = FixedPoint(0, **qformat)

for i in range(10000):
    x.from_string(hex(random.getrandbits(25)))

    # Do stuff with x that doesn't change the Q format. If the Q format
    # is changed, you could use the context manager to restore the original
    # values once the processing in this iteration is done.

overflow

Overflow occurs when a number is greater than the minimum or maximum value that the fixed point number can represent (underflow is also generalized here to overflow). This is determined by the Q format.

The FixedPoint class offers two forms of overflow handling:

  • 'clamp' (default if not specified)

  • 'wrap'

'clamp'

Clamping limits the number to the most maximum or minimum value representable by the available Q format. The overflow_alert property may need to be changed from the default 'error' to allow processing to continue.

>>> x = FixedPoint(999, 0, 4, 1, overflow='clamp')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Integer 999 overflows in UQ4.1 format.

>>> try:
...     x
... except NameError:
...     print('x was not assigned because of the FixedPointOverflowError')
x was not assigned because of the FixedPointOverflowError

>>> x = FixedPoint(999, 0, 4, 1, overflow='clamp', overflow_alert='ignore')
>>> x.clamped, x.qformat, bin(x), float(x)
(True, 'UQ4.1', '0b11111', 15.5)

>>> x = FixedPoint(999, 1, 4, 1, overflow='clamp', overflow_alert='warning')
WARNING [SN3]: Integer 999 overflows in Q4.1 format.
WARNING [SN3]: Clamped to maximum.
>>> x.qformat, hex(x), float(x)
('Q4.1', '0xf', 7.5)

>>> x = FixedPoint(-999, 0, 4, 1, overflow='clamp', overflow_alert='warning')
WARNING [SN4]: Integer -999 overflows in UQ4.1 format.
WARNING [SN4]: Clamped to minimum.
>>> x.qformat, hex(x), float(x)
('UQ4.1', '0x0', 0.0)

>>> x = FixedPoint(-999, 1, 4, 1, overflow='clamp', overflow_alert='ignore')
>>> x.qformat, bin(x), float(x)
('Q4.1', '0b10000', -8.0)

'wrap'

Wrapping will compute the full-length value and then mask away unspecified MSbs. This is also called 2’s complement overflow. The overflow_alert property may need to be changed from the default 'error' to allow processing to continue.

>>> big = 0b11000 # Needs 5 (unsigned) integer bits to represent
>>> big
24
>>> x = FixedPoint(big, 0, 4, 1, overflow='wrap')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Integer 24 overflows in UQ4.1 format.

>>> try:
...     x
... except NameError:
...     print('x was not assigned because of the FixedPointOverflowError')
x was not assigned because of the FixedPointOverflowError

>>> x = FixedPoint(big, 0, 4, 1, overflow='wrap', overflow_alert='ignore')
>>> x.clamped, x.qformat, bin(x), float(x)
(False, 'UQ4.1', '0b10000', 8.0)

>>> x = FixedPoint(big, 1, 4, 1, overflow='wrap', overflow_alert='warning')
WARNING [SN3]: Integer 24 overflows in Q4.1 format.
WARNING [SN3]: Wrapped maximum.
>>> x.qformat, hex(x), float(x)
('Q4.1', '0x10', -8.0)

>>> x = FixedPoint(-1, 0, 4, 1, overflow='wrap', overflow_alert='warning')
WARNING [SN4]: Integer -1 overflows in UQ4.1 format.
WARNING [SN4]: Wrapped minimum.
>>> x.qformat, bin(x), 15.0
('UQ4.1', '0b11110', 15.0)

>>> -big & 0b11111
8
>>> x = FixedPoint(-big, 1, 4, 1, overflow='wrap', overflow_alert='ignore')
>>> x.qformat, bin(x), float(x)
('Q4.1', '0b10000', -8.0)

rounding

Rounding occurs when non-zero fractional bits must be removed from the number. The FixedPoint class offers several forms of rounding:

  • 'convergent' (default for signed numbers if not specified)

  • 'nearest' (default for unsigned numbers if not specified)

  • 'out'

  • 'in'

  • 'up'

  • 'down'

The table below summarizes the behavior of all supported rounding schemes. When the number in the first column is rounded to 0 fractional bits using the rounding scheme in the first row, the result is shown.

Rounding Scheme Summary

convergent

nearest

down

in

out

up

-3.49

-3

-3

-4

-3

-3

-3

-3.50

-4

-3

-4

-3

-4

-3

-3.51

-4

-4

-4

-3

-4

-3

+3.49

+3

+3

+3

+3

+3

+4

+3.50

+4

+4

+3

+3

+4

+4

+3.51

+4

+4

+3

+3

+4

+4

'convergent'

Rounds toward the nearest even value in the case of a tie, otherwise rounds to the nearest value. Wikipedia outlines the pros and cons of convergent rounding.

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='convergent')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  3
 3.50 rounds to  4
 3.51 rounds to  4
 4.49 rounds to  4
 4.50 rounds to  4
 4.51 rounds to  5
-3.49 rounds to -3
-3.50 rounds to -4
-3.51 rounds to -4
-4.49 rounds to -4
-4.50 rounds to -4
-4.51 rounds to -5

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 1, 2, 4, rounding='convergent')
>>> float(x), x.qformat
(-1.625, 'Q2.4')

>>> y = FixedPoint(float(x), n=2, rounding='convergent') # round to 2 frac bits
>>> float(y), bin(y)
(-1.5, '0b1010')

Because convergent rounding can cause the value of the number to increase, it can cause overflow.

>>> x = FixedPoint(3.5)
>>> x.qformat # Can be represented with 2 integer bits
'UQ2.1'
>>> y = FixedPoint(3.5, m=2, n=0, rounding='convergent')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN2] 3.500000e+00 overflows in UQ2.0 format.

>>> z = FixedPoint(3.5, n=0, rounding='convergent') # Requires 4 integer bits
>>> float(z), z.qformat
(4.0, 'UQ3.0')

'nearest'

Rounds toward \(+\infty\) in the case of a tie, otherwise rounds to the nearest value. Wikipedia describes this widely used rounding method.

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='nearest')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  3
 3.50 rounds to  4
 3.51 rounds to  4
 4.49 rounds to  4
 4.50 rounds to  5
 4.51 rounds to  5
-3.49 rounds to -3
-3.50 rounds to -3
-3.51 rounds to -4
-4.49 rounds to -4
-4.50 rounds to -4
-4.51 rounds to -5

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 1, 2, 4, rounding='nearest')
>>> x.qformat, float(x)
('Q2.4', -1.625)

>>> y = FixedPoint(float(x), n=2, rounding='nearest') # round to 2 frac bits
>>> bin(y), float(y)
('0b1010', -1.5)

Because rounding to nearest can cause the value of the number to increase, it can cause overflow.

>>> FixedPoint(15.5).qformat # Can be represented with 4 integer bits
'UQ4.1'
>>> x = FixedPoint(15.5, m=4, n=0, rounding='nearest')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN2] 1.550000e+01 overflows in UQ4.0 format.

>>> z = FixedPoint(15.5, n=0, rounding='nearest') # Requires 5 integer bits
>>> float(z), z.qformat
(16.0, 'UQ5.0')

'out'

Rounds away from 0 in the case of a tie, otherwise rounds to the nearest value. Wikipedia outlines the pros and cons of rounding away from 0.

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='out')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  3
 3.50 rounds to  4
 3.51 rounds to  4
 4.49 rounds to  4
 4.50 rounds to  5
 4.51 rounds to  5
-3.49 rounds to -3
-3.50 rounds to -4
-3.51 rounds to -4
-4.49 rounds to -4
-4.50 rounds to -5
-4.51 rounds to -5

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 1, 2, 4, rounding='out')
>>> x.qformat, float(x)
('Q2.4', -1.625)

>>> y = FixedPoint(float(x), n=2, rounding='out') # round to 2 fractional bits
>>> bin(y), float(y)
('0b1001', -1.75)

Because rounding away from 0 causes the magnitude of the number to increase, it can cause overflow.

>>> FixedPoint(15.5).qformat # Can be represented with 4 integer bits
'UQ4.1'
>>> x = FixedPoint(15.5, m=4, n=0, rounding='out')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN2] 1.550000e+01 overflows in UQ4.0 format.

>>> z = FixedPoint(15.5, n=0, rounding='out') # Requires 5 integer bits
>>> float(z), z.qformat
(16.0, 'UQ5.0')

'in'

Rounds unconditionally toward 0.

Note

This has the same effect as truncating decimal digits.

Warning

This is not the same as truncating bits; use the down rounding scheme for bit truncation.

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='in')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  3
 3.50 rounds to  3
 3.51 rounds to  3
 4.49 rounds to  4
 4.50 rounds to  4
 4.51 rounds to  4
-3.49 rounds to -3
-3.50 rounds to -3
-3.51 rounds to -3
-4.49 rounds to -4
-4.50 rounds to -4
-4.51 rounds to -4

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 0, 2, 4, rounding='in')
>>> x.qformat, float(x)
('UQ2.4', 2.375)

>>> y = FixedPoint(float(x), n=2, rounding='in') # round to 2 fractional bits
>>> bin(y), float(y)
('0b1001', 2.25)

Because rounding in will always decrease the number’s magnitude, it cannot cause overflow.

'up'

Rounds unconditionally toward \(+\infty\).

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='up')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  4
 3.50 rounds to  4
 3.51 rounds to  4
 4.49 rounds to  5
 4.50 rounds to  5
 4.51 rounds to  5
-3.49 rounds to -3
-3.50 rounds to -3
-3.51 rounds to -3
-4.49 rounds to -4
-4.50 rounds to -4
-4.51 rounds to -4

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 0, 2, 4, rounding='up')
>>> x.qformat, float(x)
('UQ2.4', 2.375)

>>> y = FixedPoint(float(x), n=2, rounding='up') # round to 2 fractional bits
>>> bin(y), float(y)
('0b1010', 2.5)

Because rounding up can cause the number’s magnitude to increase, it can cause overflow.

>>> FixedPoint(15.5).qformat # Can be represented with 4 integer bits
'UQ4.1'
>>> x = FixedPoint(15.5, m=4, n=0, rounding='up')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN2] 1.550000e+01 overflows in UQ4.0 format.

>>> z = FixedPoint(15.5, n=0, rounding='up') # Requires 5 integer bits
>>> float(z), z.qformat
(16.0, 'UQ5.0')

'down'

Rounds unconditionally toward \(-\infty\).

Note

This is the same as truncating bits, since fractional bits cannot have negative weight.

Warning

This is not the same as truncating decimal digits; use the in rounding scheme to achieve decimal digit truncation.

>>> x = FixedPoint(0, signed=1, m=4, n=0, rounding='down')
>>> for sign in [+1, -1]:
...     for mag in [3.49, 3.5, 3.51, 4.49, 4.5, 4.51]:
...         x.from_float(init := mag * sign)
...         print(f"{init: .2f} rounds to {x: .0f}")
 3.49 rounds to  3
 3.50 rounds to  3
 3.51 rounds to  3
 4.49 rounds to  4
 4.50 rounds to  4
 4.51 rounds to  4
-3.49 rounds to -4
-3.50 rounds to -4
-3.51 rounds to -4
-4.49 rounds to -5
-4.50 rounds to -5
-4.51 rounds to -5

The example above shows easy-to-understand values and rounding off fractional bits completely. However, the same principal applies to round to a non-zero number of fractional bits:

>>> x = FixedPoint('0b100110', 0, 2, 4, rounding='down')
>>> x.qformat, float(x)
('UQ2.4', 2.375)

>>> y = FixedPoint(float(x), n=2, rounding='down')
>>> bin(y), float(y)
('0b1001', 2.25)

Because rounding down will always make the value of the number smaller in magnitude, it cannot cause overflow.

overflow_alert

The overflow_alert property indicates how you will be notified and if operation should be halted when overflow occurs. You can choose from:

  • 'error' (default if not specified)

  • 'warning'

  • 'ignore'

Overflow can occur in the following scenarios:

Some methods that can cause overflow have an alert argument which can change the notification scheme for the scope of the method. This can be used if overflow is expected.

'error'

In this notification scheme, overflow causes execution to halt and a FixedPointOverflowError is raised.

>>> x = FixedPoint(999, 0, 1, 0, overflow_alert='error')
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Integer 999 overflows in UQ1.0 format.

>>> x # Does not exist because of the exception!
Traceback (most recent call last):
    ...
NameError: name 'x' is not defined

'warning'

In this notification scheme, overflow will emit two warnings, and execution will continue. The first warning informs you of the overflow cause, the second warning informs you of the action taken.

>>> x = FixedPoint(999, 0, 1, 0, overflow='clamp', overflow_alert='warning')
WARNING [SN1]: Integer 999 overflows in UQ1.0 format.
WARNING [SN1]: Clamped to maximum.
>>> float(x), x.clamped
(1.0, True)

>>> y = FixedPoint(3, 1, 2, 0, overflow='wrap', overflow_alert='warning')
WARNING [SN2]: Integer 3 overflows in Q2.0 format.
WARNING [SN2]: Wrapped maximum.
>>> float(y), y.clamped
(-1.0, False)

'ignore'

In this notification scheme, overflow is handled silently.

>>> x = FixedPoint(3.75, 0, 2, 1, overflow_alert='ignore')
>>> float(x), x.clamped
(3.5, True)

>>> y = FixedPoint(-3, 1, 2, 10, overflow='wrap', overflow_alert='ignore')
>>> float(y), bin(y)
(1.0, '0b10000000000')

mismatch_alert

The mismatch_alert property indicates how you will be notified and if operation should be halted when any property of 2 combining FixedPoints do not match. You can choose from:

  • 'error'

  • 'warning' (default if not specified)

  • 'ignore'

For instance, what are the properties of c below?

a = FixedPoint(1,
    overflow='wrap',
    rounding='in',
    overflow_alert='error',
    mismatch_alert='warning',
    implicit_cast_alert='ignore',
    str_base=8,
)

b = FixedPoint(3.12345,
    overflow='clamp',
    rounding='convergent',
    overflow_alert='warning',
    mismatch_alert='ignore',
    implicit_cast_alert='error',
    str_base=2,
)

c = a + b

FixedPoint uses the PropertyResolver class to resolve property mismatches for the following operations:

(in case you’re curious, the example above produces the following):

WARNING [SN1]: Non-matching mismatch_alert behaviors ['ignore', 'warning'].
WARNING [SN1]: Using 'warning'.
WARNING [SN1]: Non-matching overflow behaviors ['clamp', 'wrap'].
WARNING [SN1]: Using 'clamp'.
WARNING [SN1]: Non-matching rounding behaviors ['convergent', 'in'].
WARNING [SN1]: Using 'convergent'.
WARNING [SN1]: Non-matching overflow_alert behaviors ['error', 'warning'].
WARNING [SN1]: Using 'error'.
WARNING [SN1]: Non-matching implicit_cast_alert behaviors ['error', 'ignore'].
WARNING [SN1]: Using 'error'.

Tip

You may consider starting out with mismatch_alert set to 'error', just to make sure you understand the nuances of the FixedPoint class.

'error'

In this notification scheme, the first property mismatch encountered will raise a MismatchError exception.

>>> a = FixedPoint(-1, rounding='convergent', mismatch_alert='error')
>>> b = FixedPoint(+1, rounding='nearest', mismatch_alert='error')
>>> c = a + b  
Traceback (most recent call last):
    ...
fixedpoint.MismatchError: [SN1] Non-matching rounding behaviors ['convergent', 'nearest'].

>>> c # Does not exist because of the exception!
Traceback (most recent call last):
    ...
NameError: name 'c' is not defined

If either FixedPoint’s mismatch_alert is 'error', then an exception is thrown. This is even the case if one of the settings is 'ignore' (see the example below). When there are multiple mismatched properties the first mismatch encountered in the resolution order is the culprit.

>>> a.mismatch_alert = 'ignore' # Now mismatch_alert and rounding don't match
>>> d = a + b 
Traceback (most recent call last):
    ...
MismatchError: Non-matching mismatch_alert behaviors ['error', 'ignore'].

>>> d # Does not exist because of the exception!
Traceback (most recent call last):
    ...
NameError: name 'd' is not defined

'warning'

In this notification scheme, two warnings are emitted for each mismatch, but execution will continue. The first warning informs you of the non-matching properties, the second informs you of the resolution.

>>> x = FixedPoint(1492)
>>> y = FixedPoint(x)
>>> y.rounding = 'down'
>>> z = x - y
WARNING [SN1]: Non-matching rounding behaviors ['down', 'nearest'].
WARNING [SN1]: Using 'nearest'.

>>> z.rounding
'nearest'

Warnings are emitted in the property resolution order.

>>> y.overflow = 'wrap'
>>> zz = x + y # Overflow is resolved before rounding
WARNING [SN1]: Non-matching overflow behaviors ['clamp', 'wrap'].
WARNING [SN1]: Using 'clamp'.
WARNING [SN1]: Non-matching rounding behaviors ['down', 'nearest'].
WARNING [SN1]: Using 'nearest'.

>>> zz.rounding, zz.overflow
('nearest', 'clamp')

For augmented arithmetic operations (e.g. +=), mismatches are ignored because a new FixedPoint is not being created.

>>> import sys, io
>>> sys.stderr = io.StringIO() # redirect stderr to a string buffer
>>> x -= y
>>> y *= x
>>> sys.stderr.flush()
>>> sys.stderr.getvalue() # Nothing written to stderr
''
>>> sys.stderr = sys.__stderr__ # Restore stderr

'ignore'

In this notification scheme, property mismatches are resolved silently. The same examples from above are used.

>>> x = FixedPoint(1492, mismatch_alert='ignore')
>>> y = FixedPoint(x)
>>> y.rounding = 'down'
>>> z = x - y
>>> z.rounding
'nearest'

>>> y.overflow = 'wrap'
>>> zz = x + y # Overflow is resolved before rounding
>>> zz.rounding, zz.overflow
('nearest', 'clamp')

implicit_cast_alert

Arithmetic operations allow non-FixedPoints as operands, and in such cases, are cast to a FixedPoint (see the Initializing from other types section) prior to operation. When a value is cast, its value is compared to the previous object, and if it doesn’t exactly match, FixedPoint emits an alert. The implicit_cast_alert property indicates how you will be notified and if operation should be halted when implicit casting introduces error. You can choose from:

  • 'error'

  • 'warning' (default if not specified)

  • 'ignore'

Note

Finding stimulus to actually cause this alert naturally has not been achieved. This notification scheme was unit tested using a patcher. The examples in this section employ the same technique to illustrate functionality.

Imiplicit cast alerts can conceivably be issued for the following operations:

'error'

In this notification scheme, numerical error introduced by implicit casting will raise an ImplicitCastError exception and operation is halted.

>>> a = FixedPoint(3, implicit_cast_alert='error')
>>> x = a + 0.2
Traceback (most recent call last):
    ...
fixedpoint.ImplicitCastError: [SN1] Casting 0.2 to UQ0.53 introduces an error of 5.551115e-17

>>> x # Does not exist because of the exception!
Traceback (most recent call last):
    ...
NameError: name 'x' is not defined

'warning'

In this notification scheme, numerical error introduced by implicit casting will emit a warning, but the operation is still carried out.

>>> a = FixedPoint(1969, implicit_cast_alert='warning')
>>> a -= 0.3
WARNING [SN1]: Casting 0.3 to UQ0.52 introduces an error of 5.551115e-17

>>> print(f"{a:.60f}")
1968.700000000000045474735088646411895751953125000000000000000000

'ignore'

In this notification scheme, numerical error introduced by implicit casting is ignored.

>>> a = FixedPoint(-10.5, implicit_cast_alert='ignore')
>>> x = 0.2 * a
>>> print(f"{x:.60f}")
-2.100000000000000088817841970012523233890533447265625000000000

str_base

When casting a FixedPoint to a str, the bits of the FixedPoint are displayed. Since the bits are stored internally as an int, they are simply converted using bin(), oct(), str, or hex() from the __str__() method. The str_base property indicates the base of the generated string. You can choose from:

  • 16 (default if not specified)

  • 10

  • 8

  • 2

16

Generates a hexadecimal string representative of FixedPoint.bits. The string is sign extended (or 0-padded) to the bit width of the object, and does not include the radix.

>>> x = FixedPoint(0xdeadbeef, 1, 64, 8) # str_base=16 by default
>>> str(x)
'00000000deadbeef00'
>>> str(-x)
'ffffffff2152411100'

10

Generates a decimal string representative of FixedPoint.bits. The string is not sign extended to the bit width of the object, and does not include a radix. This is equivalent to str(FixedPoint(...).bits).

>>> x = FixedPoint(2, 1, 8, str_base=10)
>>> str(x) # no zero-padding occurs
'2'
>>> x.n += 1 # Effectively multiplies the bits by 2
>>> str(x)
'4'
>>> x.n = 0
>>> str(-x) # Never negative
'254'

8

Generates an octal string representative of FixedPoint.bits. The string is sign extended (or 0-padded) to the bit width of the object, and does not include the radix.

>>> x = FixedPoint(1/3, 1, 1, str_base=8)
>>> x.qformat, str(x)
('Q1.54', '0252525252525252525')
>>> str(-x)
'1525252525252525253'

2

Generates a binary string representative of FixedPoint.bits. The string is sign extended (or 0-padded) to the bit width of the object, and does not include the radix.

>>> x = FixedPoint(-1 + 2**-40, str_base=2)
>>> x.qformat, str(x)
('Q1.40', '10000000000000000000000000000000000000001')
>>> str(-x)
'01111111111111111111111111111111111111111'

Arithmetic

Addition

FixedPoint addition using the + or += operator is always full-precision; that is, there is always bit growth.

Addition Bit Growth Summary

Augend

Addend

Sum

\(UQm_1.n_1\)
(unsigned)
\(UQm_2.n_2\)
(unsigned)
\(UQx.y\), where
\(x = 1 + max\{m_1, m_2\}\)
\(y = max\{n_1, n_2\}\)
\(Qm_1.n_1\)
(signed)
\(Qm_2.n_2\)
(signed)
\(Qx.y\), where
\(x = 1 + max\{m_1, m_2\}\)
\(y = max\{n_1, n_2\}\)
\(Qm_1.n_1\)
(signed)
\(UQm_2.n_2\)
(unsigned)
\(UQm_1.n_1\)
(unsigned)
\(Qm_2.n_2\)
(signed)

As indicated in the table, any combination of signed and unsigned numbers can be added together. The sum is only unsigned when both augend and added are unsigned.

Overflow is not possible using the addition operators.

Unsigned

>>> x = FixedPoint(14, signed=0, m=8, n=4)
>>> y = FixedPoint(6, signed=0, m=3, n=5)
>>> z = x + y
>>> print(f'  {x:q}\n+ {y:q}\n-------\n  {z:q}')
  UQ8.4
+ UQ3.5
-------
  UQ9.5

Signed

>>> x = FixedPoint(-4, signed=1, m=4, n=4)
>>> y = FixedPoint(3, signed=1, m=3, n=5)
>>> z = x + y
>>> print(f'  {x:q}\n+ {y:q}\n------\n  {z:q}')
  Q4.4
+ Q3.5
------
  Q5.5

Mixed Signedness

>>> s = FixedPoint(-4.375, signed=1, m=4, n=4)
>>> u = FixedPoint(3 + 2**-5, signed=0, m=3, n=5)
>>> z = s + u
WARNING [SN1]: Non-matching rounding behaviors ['convergent', 'nearest'].
WARNING [SN1]: Using 'convergent'.

>>> print(f'   {s:q}\n+ {u:q}\n-------\n   {z:q}')
   Q4.4
+ UQ3.5
-------
   Q5.5

Additional Examples

This behavior guarantees that addition will never cause overflow. However, it does mean that an accumulator may grow larger than intended.

For example, summing 64 Q18.0 numbers will grow at maximum \(log_2(64) = 6\) bits, thus the accumulator should be sized to 24 integer bits.

from fixedpoint import FixedPoint

accum = FixedPoint(0, 1, 24, 0, overflow_alert='error', str_base=2)
max_neg = FixedPoint(-2**17, 1, 18, 0)
assert max_neg.clamped

for _ in range(64):
    accum += max_neg
    accum.clamp(24)

print(f"{int(accum)} in {accum:q} is\n0b{accum:_b}")

Summing the maximum negative Q18.0 number 64 times produces a Q24.0 that is clamped to the maximum negative value. Note that accum.overflow_alert was set to 'error', thus we would have been informed had overflow occurred.

-8388608 in Q24.0 is
0b1000_0000_0000_0000_0000_0000

Subtraction

FixedPoint subtraction using the - or -= operator is always full-precision; that is, there is always bit growth.

Subtraction Bit Growth Summary

Minuend

Subtrahend

Difference

\(UQm_1.n_1\)
(unsigned)
\(UQm_2.n_2\)
(unsigned)
\(UQx.y\), where
\(x = 1 + max\{m_1, m_2\}\)
\(y = max\{n_1, n_2\}\)
Overflow occurs if subtrahend > minuend.
\(Qm_1.n_1\)
(signed)
\(Qm_2.n_2\)
(signed)
\(Qx.y\), where
\(x = 1 + max\{m_1, m_2\}\)
\(y = max\{n_1, n_2\}\)
\(Qm_1.n_1\)
(signed)
\(UQm_2.n_2\)
(unsigned)
\(Qx.y\), where
\(x = 2 + max\{m_1, m_2\}\)
\(y = max\{n_1, n_2\}\)
\(UQm_1.n_1\)
(unsigned)
\(Qm_2.n_2\)
(signed)

As indicated in the table, any combination of signed and unsigned numbers can be subtracted from each other. The difference is only unsigned when both augend and added are unsigned.

When signedness between minuend and subtrahend does not match, an extra integer bit is added to the unsigned term so it can be signed without overflowing.

Unsigned

>>> x = FixedPoint(14, signed=0, m=8, n=4)
>>> y = FixedPoint(6, signed=0, m=3, n=5)
>>> z = x - y
>>> print(f'  {x:q}\n- {y:q}\n-------\n  {z:q}')
  UQ8.4
- UQ3.5
-------
  UQ9.5
>>> float(z)
8.0

Overflow occurs when subtrahend > minuend.

>>> q_presub = y.qformat
>>> y.overflow_alert = 'warning'
>>> y -= x
WARNING [SN2]: Unsigned subtraction causes overflow.
WARNING [SN2]: Clamped to minimum.

>>> print(f'  {q_presub}\n- {x:q}\n-------\n  {y:q}')
  UQ3.5
- UQ8.4
-------
  UQ9.5

>>> float(y)
0.0

Signed

>>> x = FixedPoint(250 + 2**-6, signed=1)
>>> y = FixedPoint(-13 - 2**-8, signed=1)
>>> a = x - y
>>> print(f'  {x:q}\n- {y:q}\n------\n {a:q}')
  Q9.6
- Q5.8
------
 Q10.8

>>> float(a)
263.01953125
>>> b = y - x
>>> print(f'  {y:q}\n- {x:q}\n------\n {b:q}')
  Q5.8
- Q9.6
------
 Q10.8

>>> float(b)
-263.01953125
>>> a == -b
True

Overflow is not possible with signed subtraction.

Mixed Signedness

>>> s = FixedPoint(1, 1, 2)
>>> u = FixedPoint(1, 0, 2)
>>> x = u - s
WARNING [SN2]: Non-matching rounding behaviors ['convergent', 'nearest'].
WARNING [SN2]: Using 'convergent'.

>>> print(f' {u:q}\n- {s:q}\n------\n  {x:q}')
 UQ2.0
- Q2.0
------
  Q4.0

>>> float(x)
0.0

Note that even though u and s can be represented without overflow in both UQ2.0 and Q2.0 formats (their difference can too), 2 bits are still added to the maximum integer bit width for the result. This makes for deterministic bit growth. Use clamp() or wrap() to revert back to the original Q format if needed.

>>> y = s - u
WARNING [SN1]: Non-matching rounding behaviors ['convergent', 'nearest'].
WARNING [SN1]: Using 'convergent'.

>>> print(f'   {s:q}\n- {u:q}\n-------\n   {y:q}')
   Q2.0
- UQ2.0
-------
   Q4.0

>>> float(y), clamp(y, s.m).qformat
(0.0, 'Q2.0')

Overflow is not possible with mixed signedness subtraction.

Multiplication

FixedPoint multiplication using the * or *= operator is always full-precision; that is, there is always bit growth.

Multiplication Bit Growth Summary

Multiplicand

Multiplier

Product

\(UQm_1.n_1\)
(unsigned)
\(UQm_2.n_2\)
(unsigned)
\(UQx.y\), where
\(x = m_1 + m_2\)
\(y = n_1 + n_2\)
\(Qm_1.n_1\)
(signed)
\(Qm_2.n_2\)
(signed)
\(Qx.y\), where
\(x = m_1 + m_2\)
\(y = n_1 + n_2\)
\(Qm_1.n_1\)
(signed)
\(UQm_2.n_2\)
(unsigned)
\(UQm_1.n_1\)
(unsigned)
\(Qm_2.n_2\)
(signed)

Overflow is not possible using the multiplication operator.

Unsigned

>>> x = FixedPoint(10, n=2)
>>> y = FixedPoint(29, n=7)
>>> z = x * y
>>> print(f'  {x:q}\n* {y:q}\n-------\n  {z:q}')
  UQ4.2
* UQ5.7
-------
  UQ9.9

Signed

>>> x = FixedPoint(-4, signed=1, n=8)
>>> y = FixedPoint(2.5, signed=1)
>>> q = y.qformat
>>> y *= x
>>> print(f'  {q}\n* {x:q}\n------\n  {y:q}')
  Q3.1
* Q3.8
------
  Q6.9

>>> float(y)
-10.0

Mixed Signedness

>>> s = FixedPoint("0b1000", signed=1, m=3, n=1, rounding='nearest')
>>> u = FixedPoint("0b11", signed=0, m=2, n=0)
>>> z = u * s
>>> print(f"{u:.1f} * {s:.1f} = {z:.1f}")
3.0 * -4.0 = -12.0

>>> print(f' {u:q}\n* {s:q}\n------\n  {z:q}')
 UQ2.0
* Q3.1
------
  Q5.1

Exponentiation

FixedPoint exponentiation using the ** or **= operator is always full-precision; that is, there is always bit growth. Only positive integer exponents are supported.

Exponentiation Bit Growth Summary

Base

Exponent

Result

\(UQm.n\)
(unsigned)
\(p \in \mathbb{Z}^+\)
(int > 0)

\(UQx.y\)

where
\(x = p \times m\)
\(y = p \times n\)
\(Qm.n\)
(signed)

\(Qx.y\)

>>> x = FixedPoint(1.5)
>>> y = FixedPoint(-1.5)
>>> x**y # not allowed
Traceback (most recent call last):
    ...
TypeError: Only positive integers are supported for exponentiation.

>>> x **= -2 # not allowed
Traceback (most recent call last):
    ...
TypeError: Only positive integers are supported for exponentiation.

>>> 2**x # not allowed
Traceback (most recent call last):
    ...
TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'FixedPoint'

>>> a = x**4
>>> x.qformat, float(a), a.qformat
('UQ1.1', 5.0625, 'UQ4.4')

>>> b = y**3
>>> y.qformat, float(b), b.qformat
('Q2.1', -3.375, 'Q6.3')

Overflow is not possible using the power operators.

Negation & Absolute Value

Negation is achieved using the unary negation operator - (see FixedPoint.__neg__()). Absolute value (see FixedPoint.__abs__()) is achieved using the negation operator on negative numbers, thus the same behavior applies to unary negation and absolute value.

>>> x = FixedPoint(-4, m=10, overflow_alert='warning', str_base=2)
>>> float(y := -x)
4.0
>>> float(abs(x))
4.0

If the Q format can be maintained without overflow it will, (as in the example above) otherwise an overflow alert is issued, and the Q format of the result has an integer bit width one more than the original FixedPoint (as long as overflow_alert is not 'error').

>>> y.qformat
'Q10.0'
>>> x.trim(ints=True) # remove unneeded leading bits
>>> x.qformat, float(x)
('Q3.0', -4.0)

>>> yy = -x
WARNING [SN1]: Negating 0b100 (Q3.0) causes overflow.
WARNING [SN1]: Adjusting Q format to Q4.0 to allow negation.

>>> x.qformat, y.qformat, yy.qformat, float(yy)
('Q3.0', 'Q10.0', 'Q4.0', 4.0)

>>> zz = abs(x)
WARNING [SN1]: Negating 0b100 (Q3.0) causes overflow.
WARNING [SN1]: Adjusting Q format to Q4.0 to allow negation.

>>> x.qformat, y.qformat, zz.qformat, float(zz)
('Q3.0', 'Q10.0', 'Q4.0', 4.0)

Unsigned numbers cannot be negated; this behavior is intended to minimize user error. Negating an unsigned number should be intentional. The preferred method is by use of the context manager:

>>> x = FixedPoint(3, signed=0)
>>> xx = abs(x)
>>> float(xx)
3.0
>>> -x
Traceback (most recent call last):
    ...
fixedpoint.FixedPointError: Unsigned numbers cannot be negated.

>>> with x(m=x.m + 1, signed=1): # Increase integer bit width for sign
...     y = -x
>>> x.qformat, y.qformat, float(y)
('UQ2.0', 'Q3.0', -3.0)

Bitwise Operations

Bitwise operations do not cause overflow, nor do they modify the Q format.

Left Shift

Shifting bits left will cause MSbs to be lost. 0s are shifted into the LSb.

>>> x = FixedPoint('0b111000', 0, 3, 3, str_base=2)
>>> str(x << 2)
'100000'

To shift bits left and not lose bits, instead multiply the number by 2n, where n is the number of bits to shift.

>>> float(x) * 2**4
112.0
>>> y = x << 4
>>> float(y), y.qformat
(0.0, 'UQ3.3')

>>> z = x * 2**4
>>> float(z), z.qformat
(112.0, 'UQ8.3')

If the number of bits to shift is negative, a right shift is performed instead. For signed numbers, the value of the bits shifted in is the MSb. For unsigned numbers, 0s are shifted into the MSb.

>>> str(x << -2) # unsigned shift
'001110'
>>> with x(overflow_alert='ignore', overflow='wrap', signed=1): # signed shift
...     str(x << -2)
'111110'

Right Shift

Shifting bits right will cause LSbs to be lost. 0s are shifted into the MSb for unsigned numbers. Sign bits are shifted into the MSb for signed numbers.

>>> notsigned = FixedPoint('0b111000', 0, 3, 3, str_base=2)
>>> signedneg = FixedPoint('0b111000', 1, 3, 3, str_base=2)
>>> signedpos = FixedPoint('0b011000', 1, 3, 3, str_base=2)
>>> print(f"{notsigned >> 2!s}\n{signedpos >> 2!s}\n{signedneg >> 2!s}")
001110
000110
111110

To shift bits left and not lose bits, instead multiply the number by 2-n, where n is the number of bits to shift.

If the number of bits to shift is negative, a left shift is performed instead. 0s are shifted into the LSb.

>>> print(f"{notsigned >> -2!s}\n{signedpos >> -2!s}\n{signedneg >> -2!s}")
100000
100000
100000
>>> x = FixedPoint(1, m=3)
>>> 2**-3 # Desired numerical value
0.125

>>> y = x >> 3
>>> float(y), y.qformat
(0.0, 'UQ3.0')

>>> z = x * 2**-3
>>> float(z), z.qformat
(0.125, 'UQ3.3')

AND, OR, XOR

The &, &=, |, |=, ^, and ^= operators perform bitwise operations. A FixedPoint is inter operable with an int or another FixedPoint. In the latter case, the operand on the left will be the Q format of the returned value.

>>> from operator import and_, or_, xor
>>> def operate(left, op, right):
...     """Pretty display of using `op` with `left` and `right` operands"""
...     r = {'&': and_, '|': or_, '^': xor}[op](left, right)
...     l = max(len(left), len(right))
...     return (f"  {left:>{l}s} ({left:q})\n"
...             f"{op} {right:>{l}s} ({right:q})\n"
...             f"----------{'-' * l}\n"
...             f"  {r:>{l}s} ({r:q})")

>>> L = FixedPoint('0b100011', 0, 3, 3, str_base=2)
>>> R = FixedPoint(0b10, str_base=2)
>>> print(f"  L & R\n{operate(L, '&', R)}")
  L & R
  100011 (UQ3.3)
&     10 (UQ2.0)
----------------
  000010 (UQ3.3)

>>> print(f"  R | L\n{operate(R, '|', L)}")
  R | L
      10 (UQ2.0)
| 100011 (UQ3.3)
----------------
      11 (UQ2.0)

>>> print(f"  L ^ R\n{operate(L, '^', R)}")
  L ^ R
  100011 (UQ3.3)
^     10 (UQ2.0)
----------------
  100001 (UQ3.3)

When using an int as an operand, the operation is performed on the FixedPoint.bits attribute, and not the numerical value.

>>> x = FixedPoint('0b100011', 1, 3, 3, str_base=2)
>>> str(a := 7 & x)
'000011'

>>> float(a)
0.375

The order of the operands is irrelevant.

>>> str(b1 := x ^ 0b110000)
'010011'

>>> str(b2 := 0b110000 ^ x)
'010011'

>>> float(b1), float(b2)
(2.375, 2.375)

The integer is masked to the the number of bits in the FixedPoint before performing the operation.

>>> b1 |= 0b11111111111111111111101100 # (only the left len(b1) bits are used)
>>> str(b1), float(b1)
('111111', -0.125)

Inversion

Use the unary inversion operator ~ (see FixedPoint.__invert__()) to perform bitwise inversion.

>>> x = FixedPoint(0xAAAA)
>>> hex(~x)
'0x5555'

>>> ~x == (x ^ x.bitmask)
True

Bit Resizing

Attention

The code examples on this page change betwee the function version and the method version of the resizing operation. For example, y1, and y2 in the following example are equivalent, and x remains unchanged:

>>> (x := FixedPoint(-1.5)).qformat
'Q2.1'
>>> y1 = FixedPoint(x)
>>> y1.resize(10, 10)

>>> y2 = resize(x, 10, 10)

>>> print(f'{x.qformat=}\n{y1.qformat=}\n{y2.qformat=}')
x.qformat='Q2.1'
y1.qformat='Q10.10'
y2.qformat='Q10.10'

The function will operate on a copy of the given object, so the original object is not modified.

resize

Using FixedPoint.resize() or resize(), the fractional and integer bit width can grow or shrink. The bit widths are modified based on the position of the binary point, so as long as overflow or rounding does not occur, the value does not change.

>>> from fixedpoint import FixedPoint
>>> def show(A):
...     print(f"{A: <+5.2f}  ", end="")       # float
...     print(f"{A:>5q}  ", end="")           # Q format
...     print(f"{A:0{A.m+(A.m-1)//4}_bm}." if A.m else ".", end="")
...     print(f"{A:0{A.n}_bn}" if A.n else "") # Show binary point

>>> neg_signed   = FixedPoint(-2, 1, 4, 2)
>>> pos_signed   = FixedPoint(+2, 1, 4, 2)
>>> pos_unsigned = FixedPoint(+2, 0, 4, 2)

>>> show(neg_signed)
-2.00   Q4.2  1110.00

>>> show(pos_signed)
+2.00   Q4.2  0010.00

>>> show(pos_unsigned)
+2.00  UQ4.2  0010.00

Increasing the integer bit width of a negative FixedPoint will sign-extend:

>>> x = resize(neg_signed, 7, 2)
>>> show(x)
-2.00   Q7.2  111_1110.00

Increasing the integer bit width of a positive FixedPoint (sign or unsigned) will pad with zeros:

>>> x = resize(pos_signed, 7, 2)
>>> show(x)
+2.00   Q7.2  000_0010.00

>>> y = resize(pos_unsigned, 7, 2)
>>> show(y)
+2.00  UQ7.2  000_0010.00

Since fractional bits always have a positive weight (by virtue of the fact that the Q format does not allow for a non-positive integer bit width for signed numbers), increasing the fractional bit width of any (signed or unsigned) FixedPoint will pad with zeros:

>>> show(resize(neg_signed, 4, 5))
-2.00   Q4.5  1110.0_0000

>>> show(resize(pos_signed, 4, 5))
+2.00   Q4.5  0010.0_0000

>>> show(resize(pos_unsigned, 4, 5))
+2.00  UQ4.5  0010.0_0000

Decreasing the integer bit width below the minimum number of bits required to represent it will result in overflow:

>>> neg_signed.resize(1, 5)
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Overflow in format Q4.5.

Override the overflow_alert property by setting the alert argument to the desired alert level. Override the overflow property by setting the overflow argument. These overrides only take effect inside the function/method; the original property setting is restored after resizing.

>>> show(resize(neg_signed, 1, 5, alert='warning', overflow='wrap'))
WARNING [SN10]: Overflow in format Q4.5.
WARNING [SN10]: Wrapped minimum.
+0.00   Q1.5  0.0_0000

>>> show(resize(pos_signed, 1, 2, alert='warning'))
WARNING [SN11]: Overflow in format Q4.2.
WARNING [SN11]: Clamped to maximum.
+0.75   Q1.2  0.11

>>> show(resize(pos_unsigned, 1, 0, alert='ignore'))
+1.00  UQ1.0  1.

Decreasing the fractional bit width below the minimum number of bits required to represent it will result in rounding. Override the rounding property by setting the rounding argument. This override only takes effect inside the function/method; the original property setting is restored after resizing.

>>> neg_signed.rounding
'convergent'
>>> show(resize(neg_signed | 0b11, 3, 1))
-1.00   Q3.1  111.0

>>> show(resize(pos_signed | 0b11, 4, 1, rounding='down'))
+2.50   Q4.1  0010.1
>>> pos_signed.rounding
'convergent'

>>> show(resize(pos_unsigned | 0b11, 2, 0, 'out'))
+3.00  UQ2.0  11.
>>> pos_unsigned.rounding
'nearest'

Rounding can potentially cause overflow if the integer portion of the FixedPoint is already at its maximum. Only certain rounding schemes can cause this.

>>> signed_maxed = FixedPoint('0xF', 1, 3, 2) # convergent rounding
>>> show(signed_maxed)
+3.75   Q3.2  011.11

>>> show(resize(signed_maxed, 3, 0, alert='warning', overflow='wrap'))
WARNING [SN20]: Convergent round to Q3.0 causes overflow.
WARNING [SN20]: Wrapped maximum.
-4.00   Q3.0  100.

When resizing, fractional bits are resized first, followed by integer bits. This could cause issues if (for example) the number being resized originally has 0 integer bits, and you are resizing to 0 fractional bits:

>>> orig = FixedPoint(0.25, rounding='up')
>>> show(orig)
+0.25  UQ0.2  .01

>>> orig.resize(4, 0) # resize to Q4.0
Traceback (most recent call last):
    ...
ValueError: Word size (integer and fractional) must be positive.

In this case, you’ll need to manually resize the integer bit width first, then the fractional:

>>> orig.m = 4
>>> orig.n = 0
>>> show(orig)
+1.00  UQ4.0  0001.

trim

Using FixedPoint.trim() or trim() will remove superfluous/insignificant bits.

>>> neg_signed   = FixedPoint(-2, 1, 4, 2)
>>> pos_signed   = FixedPoint(+2, 1, 4, 2)
>>> pos_unsigned = FixedPoint(+2, 0, 4, 2)

>>> show(neg_signed)
-2.00   Q4.2  1110.00
>>> show(trim(neg_signed))
-2.00   Q2.0  10.

>>> show(pos_signed)
+2.00   Q4.2  0010.00
>>> show(trim(pos_signed))
+2.00   Q3.0  010.

>>> show(pos_unsigned)
+2.00  UQ4.2  0010.00
>>> show(trim(pos_unsigned))
+2.00  UQ2.0  10.

You can opt to trim off only fractional or integer bits by setting fracs or int, respectively, to True.

>>> neg_signed.trim(ints=True)
>>> show(neg_signed)
-2.00   Q2.2  10.00

>>> pos_signed.trim(fracs=True)
>>> show(pos_signed)
+2.00   Q4.0  0010.

>>> pos_unsigned.trim(True, True) # same as pos_unsigned.trim()
>>> show(pos_unsigned)
+2.00  UQ2.0  10.

Zero is always trimmed to 1 integer bit and 0 fractional bits.

>>> signed = FixedPoint(0, 1, 4, 4)
>>> signed_no_frac = FixedPoint(0, 1, 4, 0)
>>> unsigned_no_int = FixedPoint(0, 0, 0, 4)

>>> show(signed)
+0.00   Q4.4  0000.0000
>>> show(trim(signed))
+0.00   Q1.0  0.

>>> show(signed_no_frac)
+0.00   Q4.0  0000.
>>> show(trim(signed_no_frac))
+0.00   Q1.0  0.

>>> show(unsigned_no_int)
+0.00  UQ0.4  .0000
>>> show(trim(unsigned_no_int))
+0.00  UQ1.0  0.

Rounding

See the Initialization page for numerical examples on various rounding schemes. The items described here warrant more information than what those examples show.

Default rounding

When a FixedPoint is instantiated, a rounding scheme (whether defaulted or explicitly specified) is determined.

The FixedPoint.round() method and built-in round() function use the inherent rounding scheme.

>>> x = FixedPoint(1/3, n=24)
>>> x.rounding
'nearest'
>>> show(x)
+0.33  UQ0.24  .0101_0101_0101_0101_0101_0101

>>> show(round(x, 4))
+0.31  UQ0.4  .0101

>>> x.rounding = 'up'
>>> x.round(7)
>>> show(x)
+0.34  UQ0.7  .010_1011

Additionally, when shrinking the fractional bit width (via FixedPoint.n), the default rounding scheme is used.

>>> x.rounding = 'in'
>>> x.n = 3
>>> show(x)
+0.25  UQ0.3  .010

math.floor()

When given a float, math.floor() will round towards \(-\infty\) and return an int type.

>>> import math
>>> x = math.floor(1/3)
>>> x, type(x)
(0, <class 'int'>)

>>> y = math.floor(-1/2)
>>> y, type(y)
(-1, <class 'int'>)

Using math.floor() on a FixedPoint will produce the same result, but will not modify the fractional bit width. It simply sets all fractional bits to 0.

>>> x = FixedPoint(-2**-5)
>>> y = math.floor(x)
>>> show(x); show(y)
-0.03   Q1.5  1.1_1111
-1.00   Q1.5  1.0_0000

The FixedPoint.round_down() method is similar to math.floor(), but instead will change the fractional bit width. A bit mask can accomplish the same thing as math.floor() if importing math is not desired.

>>> show(x); show(x & ~(2**x.n - 1))
-0.03   Q1.5  1.1_1111
-1.00   Q1.5  1.0_0000

math.ceil()

When given a float, math.ceil() will round towards \(+\infty\) and return an int type.

>>> import math
>>> x = math.ceil(1/3)
>>> x, type(x)
(1, <class 'int'>)

>>> y = math.ceil(-1/2)
>>> y, type(y)
(0, <class 'int'>)

Using math.ceil() on a FixedPoint produces the same result. Note that this can cause overflow.

>>> x = FixedPoint(2**-5, signed=True, overflow_alert='warning')
>>> y = math.ceil(x)
WARNING [SN2]: Rounding up to Q1.0 causes overflow.
WARNING [SN2]: Clamped to maximum.
>>> show(x); show(y)
+0.03   Q1.5  0.0_0001
+0.00   Q1.0  0.

Because the fractional bit width is changed to 0, unsigned numbers with no no integer bits will raise an exception.

>>> x = FixedPoint(2**-5)
>>> show(x)
+0.03  UQ0.5  .0_0001

>>> math.ceil(x)
Traceback (most recent call last):
    ...
ValueError: Word size (integer and fractional) must be positive.

math.trunc()

When given a float, math.trunc() will round towards 0 (truncating decimal digits) and return an int type.

>>> import math
>>> x = math.trunc(0.333333333333333333)
>>> x, type(x)
(0, <class 'int'>)

>>> y = math.trunc(-0.5)
>>> y, type(y)
(0, <class 'int'>)

The truncation that math.trunc() performs on floats is the truncation of decimal digits. For FixedPoints, binary digits are truncated, effectively flooring the number. Thus the only difference between math.floor() and math.trunc() is that the latter leaves no fractional bits in the return value.

Attention

Comparison of floor/truncation rounding on floats/FixedPoints

Function

Argument Type

Truncated Digits

Rounds Towards

math.floor()

FixedPoint

binary

\(-\infty\)

float

decimal

\(-\infty\)

math.trunc()

FixedPoint

binary

\(-\infty\)

float

decimal

0

>>> x = FixedPoint(-2**-5, signed=1)
>>> y = math.trunc(x)
>>> show(x); show(y)
-0.03   Q1.5  1.1_1111
-1.00   Q1.0  1.

Because the fractional bit width is changed to 0, unsigned numbers with no no integer bits will have the integer bit width set to 1.

>>> x = FixedPoint(2**-5)
>>> show(x)
+0.03  UQ0.5  .0_0001

>>> show(math.trunc(x))
+0.00  UQ1.0  0.

Rounding-induced overflow

The following rounding schemes can cause overflow under the right circumstances:

This is because each of these schemes can increase the value of a number toward \(+\infty\) which can overflow into the integer bits. One possible workaround (if clamping/wrapping is not desired) is to manually change the integer bit width via FixedPoint.m before rounding:

>>> x = FixedPoint("0b1111", 0, 2, 2)
>>> x.round_out(1)
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Rounding out to UQ2.1 causes overflow.

>>> x.m += 1
>>> x.round_out(1)
>>> x.qformat, bin(x)
('UQ3.1', '0b1000')

Overflow-safe rounding

The following rounding schemes cannot possibly cause overflow:

Rounding beyond the fractional bit width

There may be times when you want to round away bits beyond the fractional bit width (e.g., keep the most significant 18 bits of a Q24.18 number and round off the rest). This can be done with FixedPoint.keep_msbs() or keep_msbs().

>>> x = FixedPoint('0x15555555555', 1, 24, 18, rounding='up')
>>> y = keep_msbs(x, 18, 0)
>>> print(f'{x: <6q} {x:_b}\n{y: <6q} {y:_b}')
Q24.18 1_0101_0101_0101_0101_0101_0101_0101_0101_0101_0101
Q18.0  1_0101_0101_0101_0110

Bit widths are calculated with respect to the MSb. This is not the same as y = resize(x, 18, 0) where the bit widths are calculated with respect to the current binary point position.

Overflow Handling

See the Initialization page for numerical examples on various overflow handling schemes. The items described here warrant more information than what those examples show.

Clamping/wrapping below 0 integer bits

There may be times when you want to remove MSbs and still perform clamping ( e.g. keep the least significant 18 bits of a Q18.24 number but clamp/wrap the entire value). This can be done with the FixedPoint.keep_lsbs() or keep_lsbs().

>>> x = FixedPoint('0x15555555555', 1, 18, 24)
>>> y = keep_lsbs(x, 18, 0, overflow='clamp', alert='ignore')
>>> print(f'{x: <6q} {x:_b}\n{y: <6q} {y:_b}')
Q18.24 1_0101_0101_0101_0101_0101_0101_0101_0101_0101_0101
Q18.0  1_1111_1111_1111_1111

Bit widths are calculated with respect to the LSb. This is not the same as y = resize(x, 18, 0) where the bit widths are calculated with respect to the current binary point position.

Context Management

The FixedPoint class offers richly-featured context management (see PEP 343) that allows for some unique approaches to programatic arithmetic. Three functions are utilized:

FixedPoint.__call__() allows properties to be assigned in the with statement at the start of the context; this is called context initialization.

FixedPoint.__enter__() save off the current state of the FixedPoint and assigns the properties specified by __call__() in the new context.

FixedPoint.__exit__() restores the original context unless the safe_retain keyword was specified in __call__().

Basic Usage

Use the with statement to generate a scope in which changes to the original object can be undone:

>>> from fixedpoint import FixedPoint
>>> x = FixedPoint(1/9, signed=1)
>>> x.qformat
'Q1.54'

>>> with x: # save off the current state of x
...    x.signed = 0
...    x.m = 42
...    x.qformat # show the changes that were made within the context
'UQ42.54'

>>> x.qformat # outisde of the with context, original x is restored
'Q1.54'

Any property or attribute of the original FixedPoint can be changed within the context. All changes made to FixedPoint properties are restored at context exit. These properties include:

Even the value can be changed with Arithmetic or Initializers.

>>> float(x)
0.1111111111111111

>>> with x:
...    x.from_string("0x7FFFFFAAAA5555")
...    float(x)
-7.947407237862691e-08

>>> float(x)
0.1111111111111111

New FixedPoints generated inside the context manager are valid and available outside of the context. This is useful for temporarily overriding properties. You can also rename a variable if desired.

>>> x = FixedPoint(0.2)
>>> y = FixedPoint(0.7)
>>> x.qformat, y.qformat
('UQ0.54', 'UQ0.52')

>>> z = x - y
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN3] Unsigned subtraction causes overflow.

>>> with x as xtmp, y as ytmp:
...    xtmp.m, ytmp.m = 1, 1
...    xtmp.signed, ytmp.signed = 1, 1
...    z = x - y
...    xtmp.qformat, ytmp.qformat, z.qformat
('Q1.54', 'Q1.52', 'Q2.54')

>>> x.qformat, y.qformat, z.qformat, float(round(z, 1))
('UQ0.54', 'UQ0.52', 'Q2.54', -0.5)

Context managers can be nested:

>>> def nest(x):
...     print(f'0) {x.rounding=}')
...     with x as y:
...         print(f'1) {x.rounding=}')
...         x.rounding = 'in'
...         print(f'2) {x.rounding=}')
...         with y as z:
...             print(f'3) {x.rounding=}')
...             z.rounding = 'convergent'
...             print(f'4) {x.rounding=}')
...         print(f'5) {x.rounding=}')
...     print(f'6) {x.rounding=}')

>>> nest(FixedPoint(31))
0) x.rounding='nearest'
1) x.rounding='nearest'
2) x.rounding='in'
3) x.rounding='in'
4) x.rounding='convergent'
5) x.rounding='in'
6) x.rounding='nearest'

Context Initialization

In addition to saving off the current context of FixedPoint objects, the with statement can also initialize the new context for you. Given x, y, and z below,

>>> x = FixedPoint(-1)
>>> y = FixedPoint(1, mismatch_alert='error')
>>> z = x + y
Traceback (most recent call last):
    ...
fixedpoint.MismatchError: [SN2] Non-matching mismatch_alert behaviors ['warning', 'error'].

the following two code blocks accomplish the same goal:

>>> with x, y:
...     x.rounding = 'nearest'
...     x.mismatch_alert = 'error'
...     z = x + y
>>> float(z)
0.0
>>> with x(rounding='nearest', mismatch_alert='error'):
...     z = x + y
>>> float(z)
0.0

Any keywordable argument from the FixedPoint constructor can be used in the context manager. All initilization arguments must be keyworded. The __call__() keywords can be specified in a dict if preferred.

>>> xprop = {'rounding': 'nearest', 'mismatch_alert': 'warning'}
>>> yprop = {'mismatch_alert': 'warning'}
>>> with x(**xprop), y(**yprop):
...     z = x + y

>>> x.rounding, x.mismatch_alert, y.rounding, y.mismatch_alert
('convergent', 'warning', 'nearest', 'error')

Retaining the Context

Context initialization also supports a safe_retain keyword that, when True, will not restore the original FixedPoint context as long as no exceptions occur.

>>> x = FixedPoint(3, str_base=10)
>>> x.qformat
'UQ2.0'

>>> with x(safe_retain=True):
...     x.signed = True
Traceback (most recent call last):
    ...
fixedpoint.FixedPointOverflowError: [SN1] Changing signedness on 3 causes overflow.

>>> x.signed, x.qformat # Changes were not retained because of exception
(False, 'UQ2.0')

>>> with x(m=3, safe_retain=True):
...     x.signed = True

>>> x.signed, x.qformat # Changes were retained
(True, 'Q3.0')

This is useful when several properties/attributes might change, and if all changes are made successfully, the properties should be retained. In fact, this is exactly how FixedPoint.resize() is implemented:

    def resize(self: FixedPointType, m: int, n: int, /, rounding: str = None,
               overflow: str = None, alert: str = None) -> None:
        """Resize integer and fractional bit widths.

        Overflow handling, sign-extension, and rounding are employed.

        Override rounding, overflow, and overflow_alert settings for the
        scope of this method by specifying the appropriate arguments.
        """
        old = self._overflow, self._rounding, self._overflow_alert
        try:
            with self(safe_retain=True,
                      overflow=overflow or self.overflow,
                      rounding=rounding or self.rounding,
                      overflow_alert=alert or self.overflow_alert):
                self.n = n
                self.m = m
        except Exception:
            raise
        else:
            self._overflow, self._rounding, self._overflow_alert = old

The magic here is that if self.m = m raises an exception, then the assignment on the line just before it is undone by the context manager. However, if no exception occurs, then the assignments to the m and n attributes are kept and the number is resized.

Bit Slicing

Sometimes you want to access only certain bits of a FixedPoint number. The FixedPoint.bits attribute returns a FixedPointBits object, which is an int that support square bracket access.

Bit Random Access

One or more contiguous bits in the FixedPointBits can be accessed with an int or slice key.

Single-Bit Random Access

To access a single bit use an integer key. Bits are indexed with the MSb being index m+n-1 and the LSb being index 0.

>>> x = FixedPoint('0b001001', signed=0, m=3, n=3, str_base=2)
>>> bin(x.bits), x.bits[3]
('0b1001', 1)

You can also access a single bit using a slice when start and stop values are equal. The slice step must be either

  • -1 (indicating a descending range with the MSb as index m+n-1 and the LSb as index 0)

  • +1 (indicating an ascending range with the MSb as index 0 and the LSb as index m+n-1)

>>> x = FixedPoint('0b001000', signed=0, m=3, n=3)
>>> x.bits[3:3:-1] # the middle '1' with a descending range
1
>>> x.bits[2:2:1] # the middle '1' with an ascending range
1

Attempting to access a single bit in this fashion (the slice start and stop are equal) without specifying a step results in an error.

>>> x.bits[3:3]
Traceback (most recent call last):
    ...
IndexError: Step must be 1 or -1 for equivalent start and stop bound 3.

Multi-Bit Random Access

To access multiple bits at a time, slices are employed. Both ascending and descending ranges are supported.

>>> x = FixedPoint(0b0001100, m=7)
>>> x.bits[3:2] # Access the middle two 1s using a descending range
3
>>> x.bits[3:2:-1] # The step can be -1 for clarity but is unnecessary
3
>>> x.bits[3:4] # Access the middle two 1s using an ascending range
3
>>> x.bits[3:4:1] # The step can be +1 for clarity but is unnecessary
3

When a step is used that is not 1 or -1, or when the start/stop index is negative, the slice accesses the bits as if they were a str.

>>> x = FixedPoint(0b100_100_100_100)
>>> x.bits[::3] # Get every 3rd bit starting from the first
15
>>> bin(x.bits[:-6]) # Get the last 6 bits
'0b100100'

Bit Mappings

Common parts of the FixedPoint bit string are mapped to keys, specified as strings (like a dict).

These include:

  • integer bits ('m' or 'int')

  • fractional bits ('n' or 'frac')

  • sign bit ('s' or 'sign')

  • most significant bit ('msb')

  • least significant bit ('lsb')

If the portion of the FixedPoint bits does not exist (e.g., the sign bit of an unsigned number), a KeyError is raised.

>>> intonly = signed = FixedPoint("0b1110", 1, 4, 0)
>>> fraconly = unsigned = FixedPoint("0b0001", 0, 0, 4)
>>> intonly.bits['m']
14
>>> fraconly.bits['int']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'int' for UQ0.4 format."

>>> intonly.bits['n']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'n' for Q4.0 format."
>>> signed.bits['sign']
1
>>> (-signed).bits['s']
0
>>> unsigned.bits['sign']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'sign' for UQ0.4 format."

>>> intonly.bits['msb'], intonly.bits['lsb']
(1, 0)
>>> fraconly.bits['msb'], fraconly.bits['lsb']
(0, 1)

When the mappings are uppercased, a str type is returned corresponding to the binary bits.

>>> intonly = signed = FixedPoint("0b1110", 1, 4, 0)
>>> fraconly = unsigned = FixedPoint("0b0001", 0, 0, 4)
>>> intonly.bits['M']
'1110'
>>> fraconly.bits['INT']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'INT' for UQ0.4 format."

>>> intonly.bits['N']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'N' for Q4.0 format."
>>> signed.bits['SIGN']
'1'
>>> (-signed).bits['S']
'0'
>>> unsigned.bits['SIGN']
Traceback (most recent call last):
    ...
KeyError: "Invalid bit specification 'SIGN' for UQ0.4 format."

>>> intonly.bits['MSB'], intonly.bits['LSB']
('1', '0')
>>> fraconly.bits['MSB'], fraconly.bits['LSB']
('0', '1')

String Conversion and Formatting

When you need to generate a string representation of a FixedPoint number (such as stimulus generation for a test), you can use standard string conversion (via str()) or a more advanced string formatting using a standard format specifier with str.format(), format(), or f-strings. These methods are described below.

String Conversion

Calling str() on a FixedPoint generates a decimal, binary, octal, or hexadecimal string based on the FixedPoint.bits. The latter 3 of which are 0-padded (if necessary) to the bit length of the FixedPoint number. No radix is included.

>>> init = '0b11001'
>>> b = FixedPoint(init, 1, 3, 2, str_base=2)
>>> str(b)
'11001'
>>> o = FixedPoint(init, 1, 3, 2, str_base=8)
>>> str(o)
'31'
>>> d = FixedPoint(init, 1, 3, 2, str_base=10)
>>> str(d)
'25'
>>> h = FixedPoint(init, 1, 3, 2, str_base=16)
>>> str(h)
'19'

String Formatting

A FixedPoint can be formatted as a str, float, or int would, depending on the format string syntax.

Standard Format Specifier Parsing Summary
format_spec
type

Formatted Type

Formatted Value
(given x = FixedPoint(...))

's'

str

str(x)
(depends on x.str_base)

'q'

x.qformat

'b'
(binary)

int

x.bits

'd'
(decimal)
'o'
(octal)
'x'
(lowercase
hexadecimal)
'X'
(uppercase
hexadecimal)

'...m' 1

x.bits['int']
(integer bits only)

'...n' 1

x.bits['frac']
(fractional bits only)

'e'

float

float(x)

'E'

'f'

'F'

'g'

'G'

'%'

1 Append to the specifier of another formatted int. E.g., 'bn' would format the fractional bits of x in binary.

str() Output

As described above, str() generates a str from the FixedPoint.bits in the base specified by FixedPoint.str_base. This can be further formatted with the format specification mini language using the format_spec type 's'.

The example below uses a formatted string literal (or f-string) to further format the output of str().

>>> x = FixedPoint(0b1010_0101_1111, str_base=2)
>>> str(x) # This is the str() output
'101001011111'

>>> f"{x:_^16s}" # Center str(x) in a field of underscores 16 characters wide
'__101001011111__'

>>> x.str_base = 16
>>> f"{x:_^16s}" # Same thing but uses hex instead of binary due to str_base
'______a5f_______'

The same thing can be done using str.format():

>>> x.str_base = 8 # octal
>>> '{:-^16s}'.format(x)
'------5137------'

or format():

>>> x.str_base = 10
>>> format(x, '~>6s')
'~~2655'

The remaining examples will be done using f-strings, but the same syntax applies to str.format() and format().

Q Format

Using the format_spec type 'q' allows you to format the FixedPoint.qformat output as a string.

>>> a = FixedPoint(-12345.678)
>>> f"{a:q}"
'Q15.36'

>>> f"{a: ^10q}"
'  Q15.36  '

FixedPoint.bits

Using the format_spec type 'b', 'o', 'd', 'x', or 'X' allow you to format the FixedPoint.bits as an int.

>>> a = FixedPoint(0b1111_0000_1011, m=14)
>>> f"{a:#0{2 + len(a) // 4 + len(a)}_b}" # add 2 for radix, // 4 for seperators
'0b00_1111_0000_1011'

>>> f"{a:010,d}"
'00,003,851'

>>> f"{a:=^+#10X}"
'==+0XF0B=='

Integer and Fractional Bits

Using the format_spec type 'm' or 'n' allows you to format the integer and fractional bits as an int. Precede these types with other standard types like 'b', 'o', 'd', 'n', 'x', or 'X'.

>>> a = FixedPoint('0b11001100', signed=0, m=4, n=5)
>>> f"{a:#0{a.m+2}bm}.{a:0{a.n}bn}" # Show the binary point
'0b0110.01100'

Float Equivalent

Using the format_spec type 'e', 'E', 'f', 'F', 'g', 'G', or '%' allow you to format the FixedPoint as a float.

>>> a = FixedPoint(1.125, rounding='up')
>>> f"{a:#0{2+a.m}bm}.{a:0{a.n}bn} ==> {a:.3f}"
'0b1.001 ==> 1.125'

>>> f"{a:.2f}" # Show 2 decimal points
'1.12'

Attention

Convergent rounding is used for rounding floats in the example above, which is the default rounding method specified by IEEE 754. This will be the case regardless of the FixedPoint.rounding property specified.

>>> f"{a:.3%}"
'112.500%'

>>> f"{a - 1:.2%}"
'12.50%'

>>> import sys
>>> b = FixedPoint(2**sys.float_info.min_exp)
>>> f"{b:+e}"
'+4.450148e-308'

Miscellaneous

Logging Integration

The fixedpoint package uses standard logging constructs to generate warnings and debug logs. The following specifications allow you to customize the logging schemes, handlers, formats, etc. to your liking.

Warnings

fixedpoint warnings are generated using the logging package.

fixedpoint.logging.WARNER_CONSOLE_HANDLER
Type

logging.StreamHandler

Value
  • stream is set to sys.stderr

  • level is set to logging.DEBUG.

fixedpoint.logging.WARNER
Type

logging.Logger

Value
  • name is FP.CONSOLE

  • level defaults to logging.WARNING

You can retrieve this logger with logging.getLogger("FP.CONSOLE").

The WARNER_CONSOLE_HANDLER is added to WARNER.

Additionally, each FixedPoint object has a unique serial number associated with it that is available in the LogRecord instance as the key sn. This is described in more detail here as the extra keyword.

Debug Logging

Logging is also used for debug purposes.

fixedpoint.logging.LOGGER
Type

logging.Logger

Value
  • name is FP

  • level defaults to logging.CRITICAL

fixedpoint.logging.DEFAULT_FILE_HANDLER
Type

logging.FileHandler

Value
  • filename is set to fixedpoint.log located in the same directory as the source code

  • mode is set to ‘w’

  • delay is set to True, thus no file is generated (or overwritten) until logging is enabled with fixedpoint.FixedPoint.enable_logging()

  • level defaults to logging.DEBUG

The DEFAULT_FILE_HANDLER is added to the LOGGER.

On initial import, LOGGER’s level is set to logging.CRITICAL. Since no critical level logs are made within fixedpoint, it essentially disables debug logging.

When FixedPoint.enable_logging() is called, LOGGER’s level is set to logging.DEBUG.

When FixedPoint.disable_logging() is called, LOGGER’s level is set back to logging.CRITICAL.

Typing

The fixedpoint package is typed (see PEP 484) and supported by mypy, PyCharm, etc.

Subclassing FixedPoint is also supported.

Numpy Integration

While not specifically tested, integration with numpy should be possible as long as unsupported operators (like @, /, //, %, etc.) are not used.

Examples taken from the numpy.convolve documentation:

>>> import numpy as np
>>> a = [FixedPoint(1), FixedPoint(2), FixedPoint(3)]
>>> b = [FixedPoint(0), FixedPoint(1), FixedPoint(0.5)]
>>> x = np.convolve(a, b)
>>> [float(fp) for fp in x]
[0.0, 1.0, 2.5, 4.0, 1.5]

>>> y = np.convolve(a, b, 'same')
>>> [float(fp) for fp in y]
[1.0, 2.5, 4.0]

>>> z = np.convolve(a, b, 'valid')
>>> [float(fp) for fp in z]
[2.5]

Serialization

JSON

class fixedpoint.json.JSONEncoder
class fixedpoint.json.JSONDecoder
>>> from fixedpoint.json import FixedPointEncoder, FixedPointDecoder
>>> import random, json
>>> L = 52
>>> signed = random.randrange(2)
>>> m = random.randrange(L)
>>> n = L - m
>>> original = FixedPoint(hex(random.getrandbits(L)), signed, m, n)
>>> serialized = json.dumps(original, cls=FixedPointEncoder)
>>> deserialized = json.loads(serialized, cls=FixedPointDecoder)
>>> original == deserialized
True

Pickle

The pickle scheme works out of the box:

>>> import random, pickle
>>> L = 52
>>> signed = random.randrange(2)
>>> m = random.randrange(signed, L)
>>> n = L - m
>>> original = FixedPoint(hex(random.getrandbits(L)), signed, m, n)
>>> pickled = pickle.dumps(original)
>>> unpickled = pickle.loads(pickled)
>>> original == unpickled
True

Indices and tables

License

The fixedpoint package is released under the BSD license.

Copyright (c) 2019-2020, Schweitzer Engineering Laboratories, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.