francof2a / fxpmath

A python library for fractional fixed-point (base 2) arithmetic and binary manipulation with Numpy compatibility.
MIT License
183 stars 21 forks source link

Wrapping and Bias issues #44

Closed desvdp closed 3 years ago

desvdp commented 3 years ago

Hi Franco, thanks for fixing the previous issues so quickly! Looking a bit closer, I did find some extra:

(1) Bias and scaling don't seem to play very wel with wrapping/saturation:

>>> b= Fxp(20.5,False, n_word=5 ,scaling=1, bias=8)
>>> b
fxp-u5/1(20.5)  ----> Ok
>>> b.bin() 
'11001'               ----> Ok

>>> b= Fxp(20.5,False, n_word=4 ,scaling=1, bias=8)
>>> b
fxp-u4/0(20.0)    ---> Had expected a saturation ie 15/2+8 = 15.5
>>> b.bin()
'1100'                  ---> Saturation? 1111

(2) There seems to be an issue with zero values:

>>> zero = Fxp(0.0,False, n_word=5 ,overflow='wrap', scaling=1, bias=8)     --> The range is [8, 23.5] ie 0 is out of range 
>>> zero
fxp-u5/0(32.0)        --> But so is 32 ... 
>>> zero.bin()
'11000'
>>> zero.info(verbose=3)
    dtype       =   fxp-u5/0
    Value       =   32.0
    Scaling     =   1 * val + 8
    underflow   =   True
    inaccuracy  =   True

    Signed      =   False
    Word bits   =   5
    Fract bits  =   0
    Int bits    =   5
    Val data type   =   <class 'float'>

    Upper       =   39.0
    Lower       =   8.0
    Precision   =   1.0
    Overflow    =   wrap
    Rounding    =   trunc
    Shifting    =   expand

Saturation does deal with it:

>>> zero = Fxp(0,False, n_word=5 ,overflow='saturate', scaling=1, bias=8)
>>> zero
fxp-u5/0(8.0) --> Ok 

(3) Last but not least, some long values still throw an error: b= Fxp(0,False,64,0,overflow='wrap', bias = 8)

Let me know if you need further help testing ...

Thanks !

francof2a commented 3 years ago

Hello @desvdp Thank you for the feedback.

I'm going to answer point by point while I review them.

1

In this case:

b = Fxp(20.5, False, n_word=4, scaling=1, bias=8)

fxp-u4/0(20.0)

fxpmath should define n_frac and prioritize fit the value instead of keeping enough resolution. In other words, it defines first n_int (bits for integer part). If n_int = 4, scaled range will be from 8 to 23 (the original value is in the range), but if we want to keep precision n_frac = 1 therefore n_int = 3, in this last case scaled range is from 8.0 to 15.5, saturating 20.5 to highest value of the range.

You can check this doing:

b = Fxp(20.5, False, n_word=4, n_frac=1, scaling=1, bias=8)

fxp-u4/1(15.5)

francof2a commented 3 years ago

2

In this case

zero = Fxp(0.0, False, n_word=5, overflow='wrap', scaling=1, bias=8)

fxp-u5/0(32.0)

The behavior is similar what explained in the previous reply. As 0.0 is outside the range, fxpmath choose n_int = 5 (highest possible size) and n_frac = 0 trying to fit the number. That's the reason range is 8 up to 39 (8 + 2**5 - 1). Considering this range, the value 0.0 is wrapped to 32; the value 7 will be wrapped to 39, and so on.

If you force n_frac = 1 you will get:

zero = Fxp(0.0, False, n_word=5, n_frac=1, overflow='wrap', scaling=1, bias=8)

fxp-u5/1(16.0)

The result is ok.

zero.info(verbose=3)

dtype = fxp-u5/1 Value = 16.0 Scaling = 1 * val + 8 underflow = True inaccuracy = True

Signed = False Word bits = 5 Fract bits = 1 Int bits = 4 Val data type = <class 'float'>

Upper = 15.5 Lower = 0.0 Precision = 0.5 Overflow = wrap Rounding = trunc Shifting = expand

The info is not ok in values of Upper and Lower (should be 23.5 and 8.0). So, there is a bug here.

francof2a commented 3 years ago

3

Crossing the portal to the "beyond 64 bits" always have surprises with Numpy. I've already reproduced the error and found the way to fix. So, in the next release will be included. By the way, I think more bugs are hide in scaling methods, even more using more than 64 bits, I guess. fxpmath is poorly tested in scaled arithmetic.

Thanks for your revision.

francof2a commented 3 years ago

Closing the issue because version 0.4.4 should solve it! Feel free to reopen the issue if the problem remains. Thanks!