Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions fixedpoint/fixedpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,10 +676,6 @@ def __unsupported(*args: Any, **kwargs: Any) -> NotImplemented:
"""Unsupported FixedPoint method."""
return NotImplemented

# For future reference, in case division is needed:
# https://courses.cs.washington.edu/courses/cse467/08au/labs/l5/fp.pdf
__floordiv__ = __rfloordiv__ = __ifloordiv__ = __unsupported
__truediv__ = __rtruediv__ = __itruediv__ = __unsupported
__matmul__ = __rmatmul__ = __imatmul__ = __unsupported
__mod__ = __rmod__ = __imod__ = __unsupported
__rlshift__ = __rrshift__ = __unsupported
Expand Down Expand Up @@ -814,6 +810,58 @@ def __imul__(self: FixedPointType, multiplier: Numeric) -> FixedPointType:
self._bits, self._signed, self._m, self._n = self.__mul(other)
return self

def __floordiv(numerator: FixedPointType,
denominator: FixedPointType) -> AttrReturn:
"""Perform division and return attributes of the result.
This is the
"""
m: int = numerator._m + denominator._n + 1
n: int = numerator._n + denominator._m
signed: bool = bool(numerator._signed or denominator._signed)

bits = int((numerator._signedint << (denominator._n + denominator._m)) // denominator._signedint)
return bits & (2**(m + n) - 1), signed, m, n

def __floordiv__(self: FixedPointType, other: Numeric) -> FixedPointType:
denominator, props = self.__to_FixedPoint_resolved(other)
return self.__class__.__new(*self.__floordiv(denominator), **props)

def __ifloordiv__(self: FixedPointType, denominator: Numeric) -> FixedPointType:
other = self.__to_FixedPoint(denominator)
self._bits, self._signed, self._m, self._n = self.__floordiv(other)
return self

def __rfloordiv__(self: FixedPointType, numerator: Numeric) -> FixedPointType:
other = self.__to_FixedPoint(numerator, self._signed)
return self.__class__.__new(*other.__floordiv(self, self.overflow,
self._owarn),
self.overflow, self.rounding, self.str_base,
self.overflow_alert,
self.implicit_cast_alert,
self.mismatch_alert)

# Both division operators use the maximum precision possible - it's up to the user to truncate
# the result how they see fit.
__truediv = __floordiv

def __truediv__(self: FixedPointType, other: Numeric) -> FixedPointType:
denominator, props = self.__to_FixedPoint_resolved(other)
return self.__class__.__new(*self.__truediv(denominator), **props)

def __itruediv__(self: FixedPointType, denominator: Numeric) -> FixedPointType:
other = self.__to_FixedPoint(denominator)
self._bits, self._signed, self._m, self._n = self.__truediv(other)
return self

def __rtruediv__(self: FixedPointType, numerator: Numeric) -> FixedPointType:
other = self.__to_FixedPoint(numerator, self._signed)
return self.__class__.__new(*other.__truediv(self, self.overflow,
self._owarn),
self.overflow, self.rounding, self.str_base,
self.overflow_alert,
self.implicit_cast_alert,
self.mismatch_alert)

def __pow(self: FixedPointType, exponent: int) -> AttrReturn:
"""Perform exponentiation and return attributes of the result."""
if not (isinstance(exponent, int) and exponent > 0):
Expand Down
37 changes: 37 additions & 0 deletions tests/test_operators/test_division.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
# This test was not written by the original authors of the fixedpoint library and does not conform
# to its quality standards.

import pathlib
import sys
sys.path.insert(1, str(pathlib.Path('./../..').resolve()));
import fixedpoint as uut

from random import *
import math

# fuzz test divide
for i in range(1000):
x = uut.FixedPoint(0, m=int(random() * 100)+1, n=int(random() * 100)+1, signed=1)
y = uut.FixedPoint(0, m=int(random() * 100)+1, n=int(random() * 100)+1, signed=1)

# because we are choosing only numbers that are precisely representable in fixed-point,
# no error is introduced at these steps.
x._bits = getrandbits(x._m + x._n + int(x._signed))
y._bits = getrandbits(y._m + y._n + int(y._signed))
if (y._bits == 0): y._bits = 1
d = x / y

xf = float(x)
yf = float(y)
df = xf / yf

# TODO: establish actual error bounds
MANTISSABITS = 52
exp = math.frexp(df)[1]
tol = (2**(exp - MANTISSABITS))
err = df - float(d)
if (math.fabs(err) > tol):
print(f"division of {x.qformat} / {y.qformat} has error {err}")
print(f"d = {float(d)}, df = {xf / yf}, tol = {tol}")
print()