fxpmath : substract two unsigned into a signed result

43 Views Asked by At
import numpy as np
from fxpmath import Fxp as fxp

t1 = fxp(4, False, 9, 3)
t2 = fxp(5, False, 9, 3)
inter1 = fxp(0, True, 9, 3, raw=True)
inter2 = fxp(0, True, 9, 3, raw=True)

inter1(t1 - t2)
inter2(t1.get_val() - t2.get_val())

inter1 is equal to 0.00 because the two numbers used in the calculation are unsigned. The only way I found to have the correct result is to use numpy values (from get_val()). Is there a best way to do this ? I don't want to put t1 or t2 in signed.

1

There are 1 best solutions below

0
francof2a On

The solution proposed by @jasonharper is right, but it might not be what you're trying to model.

When you do:

t1.like(inter1) - t2.like(inter1) # t1_signed - t2_signed

fxp-s10/3(-1.0)

You are converting t1 and t2 to signed just as inter1, so you are performing a signed subtraction, and I think it's not what you want to model.

Casting an unsigned fxp to a signed one force msb (most significant bit) to 0 if you keep size. That will generate a wrong calculation if t1 and/or t2 are equal or bigger than signed upper limit (31.875 for your example). For example:

t3 = fxp(48, False, 9, 3)
t4 = fxp(49, False, 9, 3)

t3.like(inter1) - t4.like(inter1) # t3_signed - t4_signed
# t3_signed = fxp-s9/3(31.875) because overflow clipping
# t4_signed = fxp-s9/3(31.875) because overflow clipping

fxp-s10/3(0.0)

If you are trying to model a unsigned subtraction and then storing that value in a signed fxp you have to use wrap overflow. Additionally, if you wanna keep size at subtraction (or other operation) you have to set op_sizing = 'same':

t1 = fxp(4, False, 9, 3, overflow='wrap', op_sizing='same')
t2 = fxp(5, False, 9, 3, overflow='wrap', op_sizing='same')

t1 - t2

fxp-u9/3(63.0)

If you check the binary format, you'll see that the subtraction is correct:

(t1 - t2).bin(frac_dot=True)

'111111.000'

Look that the msb is equal to 1, so when you cast this value as a signed fxp this will be negative:

# cast from unsigned to signed keeping raw binary value
inter1('0b' + (t1 - t2).bin(frac_dot=True)) 

fxp-s9/3(-1.0)

Now,

t3 = fxp(48, False, 9, 3, overflow='wrap', op_sizing='same')
t4 = fxp(49, False, 9, 3, overflow='wrap', op_sizing='same')
inter1('0b' + (t3 - t4).bin(frac_dot=True))

fxp-s9/3(-1.0)

Finally, let me write a more elegant way to work with same fxp sizes along your code:

from fxpmath import Fxp

# Set a template for new Fxp objects
Fxp.template = Fxp(dtype='fxp-u9/3', overflow='wrap', op_sizing='same')

inter1 = Fxp(None, signed=True) # overwrite template's sign

t1 = Fxp(4)
t2 = Fxp(5)
inter1('0b' + (t1 - t2).bin(True)) # or inter1('0b' + (Fxp(4) - Fxp(5)).bin(True))

fxp-s9/3(-1.0)