Skip to content

Commit

Permalink
Fix complex truediv and floordiv methods (issue #53).
Browse files Browse the repository at this point in the history
  • Loading branch information
francof2a committed Feb 14, 2022
1 parent 338494f commit 86344b7
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 2 deletions.
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
version 0.4.6
--------------------------------------------------
* Fix complex `truediv` and `floordiv` methods (issue #53).

version 0.4.5
--------------------------------------------------
* Fix FutureWarning in subdtype 'str' comparison (issue #45).
Expand Down
2 changes: 1 addition & 1 deletion fxpmath/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '0.4.5'
__version__ = 'dev-0.4.6'

import sys
import os
Expand Down
34 changes: 34 additions & 0 deletions fxpmath/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,35 @@ def floordiv(x, y, out=None, out_like=None, sizing='optimal', method='raw', **kw
"""
def _floordiv_repr(x, y):
return x // y

def _floordiv_repr_complex(x, y):
y_norm = y.real ** 2 + y.imag ** 2
real_part = (x.real * y.real + x.imag * y.imag) // y_norm
imag_part = (x.imag * y.real - x.real * y.imag) // y_norm
return real_part + 1j*imag_part

def _floordiv_raw(x, y, n_frac):
precision_cast = (lambda m: np.array(m, dtype=object)) if n_frac >= _n_word_max else (lambda m: m)
return ((x.val * precision_cast(2**(n_frac - x.n_frac))) // (y.val * precision_cast(2**(n_frac - y.n_frac)))) * precision_cast(2**n_frac)

def _floordiv_raw_complex(x, y, n_frac):
precision_cast = (lambda m: np.array(m, dtype=object)) if n_frac >= _n_word_max else (lambda m: m)
y_norm = (y.val.real ** 2 + y.val.imag ** 2) * precision_cast(2**(n_frac - 2*y.n_frac))
real_part = (x.val.real * y.val.real + x.val.imag * y.val.imag) * precision_cast(2**(n_frac - x.n_frac - y.n_frac)) // y_norm
imag_part = (x.val.imag * y.val.real - x.val.real * y.val.imag) * precision_cast(2**(n_frac - x.n_frac - y.n_frac)) // y_norm

return (real_part + 1j*imag_part) * precision_cast(2**n_frac)


if not isinstance(x, Fxp):
x = Fxp(x)
if not isinstance(y, Fxp):
y = Fxp(y)

if x.vdtype == complex or y.vdtype == complex:
_floordiv_repr = _floordiv_repr_complex
_floordiv_raw = _floordiv_raw_complex

signed = x.signed or y.signed
n_int = x.n_int + y.n_frac + signed
n_frac = 0
Expand All @@ -392,16 +412,30 @@ def truediv(x, y, out=None, out_like=None, sizing='optimal', method='raw', **kwa
"""
def _truediv_repr(x, y):
return x / y

def _truediv_raw(x, y, n_frac):
precision_cast = (lambda m: np.array(m, dtype=object)) if n_frac >= _n_word_max else (lambda m: m)
return (x.val * precision_cast(2**(n_frac - x.n_frac + y.n_frac))) // y.val
# return np.floor_divide(np.multiply(x.val, precision_cast(2**(n_frac - x.n_frac + y.n_frac))), y.val)

def _truediv_raw_complex(x, y, n_frac):
precision_cast = (lambda m: np.array(m, dtype=object)) if n_frac >= _n_word_max else (lambda m: m)

y_norm = y.val.real ** 2 + y.val.imag ** 2
real_part = (x.val.real * y.val.real + x.val.imag * y.val.imag) * precision_cast(2**(n_frac - x.n_frac + y.n_frac)) // y_norm
imag_part = (x.val.imag * y.val.real - x.val.real * y.val.imag) * precision_cast(2**(n_frac - x.n_frac + y.n_frac)) // y_norm

return real_part + 1j*imag_part


if not isinstance(x, Fxp):
x = Fxp(x)
if not isinstance(y, Fxp):
y = Fxp(y)

if x.vdtype == complex or y.vdtype == complex:
_truediv_raw = _truediv_raw_complex

signed = x.signed or y.signed
n_int = x.n_int + y.n_frac + signed
n_frac = x.n_frac + y.n_int
Expand Down
92 changes: 92 additions & 0 deletions tests/test_complex_numbers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import fxpmath as fxp
from fxpmath.objects import Fxp

import numpy as np

def test_complex_creation():
x = Fxp(0.25 - 1j*14.5)
assert x() == 0.25 - 1j*14.5
assert x.real == 0.25
assert x.imag == -14.5
assert x.dtype == 'fxp-s7/2-complex'
assert x.vdtype == complex

x = Fxp(3.0, dtype='fxp-s8/4-complex')
assert x() == 3.0
assert x.imag == 0.0

x = Fxp(1j*3.0, dtype='fxp-s8/4-complex')
assert x() == 1j*3.0
assert x.real == 0.0
assert x.imag == 3.0

x = Fxp([0.0, 1.0 + 1j*1.0, -1j*2.5], signed=True, n_word=8)
assert x.dtype == 'fxp-s8/1-complex'
assert x[0]() == 0.0
assert x[1]() == 1.0 + 1j*1.0
assert x[2]() == -1j*2.5

x = Fxp(0.25 - 1j*14.5, dtype='Q6.4')
assert x.dtype == 'fxp-s10/4-complex'

def test_math_operations():
c = 2.0
x = 0.25 - 1j*14.5
y = -1.0 + 1j*0.5

x_fxp = Fxp(x, dtype='Q14.3')
y_fxp = Fxp(y, dtype='Q14.3')

# add
z = x + y
z_fxp = x_fxp + y_fxp
assert z_fxp() == z

z = x + c
z_fxp = x_fxp + c
assert z_fxp() == z

# sub
z = x - y
z_fxp = x_fxp - y_fxp
assert z_fxp() == z

z = x - c
z_fxp = x_fxp - c
assert z_fxp() == z

# mul
z = x * y
z_fxp = x_fxp * y_fxp
assert z_fxp() == z

z = x * c
z_fxp = x_fxp * c
assert z_fxp() == z

# div
z = x / y
z_fxp = x_fxp / y_fxp
assert z_fxp() == z

z = x / c
z_fxp = x_fxp / c
assert z_fxp() == z

# floor div
x = np.asarray(x)
y = np.asarray(y)
z = (x * y.conj()).real // (y * y.conj()).real + 1j* ((x * y.conj()).imag // (y * y.conj()).real)
z_fxp = x_fxp // y_fxp
assert z_fxp() == z

c = np.asarray(c)
z = (x * c.conj()).real // (c * c.conj()).real + 1j* ((x * c.conj()).imag // (c * c.conj()).real)
z_fxp = x_fxp // c
assert z_fxp() == z


8 changes: 7 additions & 1 deletion tests/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,10 @@ def test_issue_44_v0_4_3():
assert b() == 8

b = Fxp(2**64+6, False, 64, 0, overflow='wrap', scaling=2, bias=8)
assert b() == 2**64+6
assert b() == 2**64+6

def test_issue_53_v0_4_5():
x = Fxp(2j, dtype = 'fxp-u4/0-complex')
z = x/2

assert z() == 1j

0 comments on commit 86344b7

Please sign in to comment.