GithubHelp home page GithubHelp logo

rwpenney / spfpm Goto Github PK

View Code? Open in Web Editor NEW
64.0 3.0 14.0 234 KB

Package for performing fixed-point, arbitrary-precision arithmetic in Python.

License: Other

Python 99.23% Makefile 0.77%
python numerical-methods fixed-point arithmetic

spfpm's Introduction

Simple Python Fixed-Point Module (SPFPM)

spfpm is a pure-Python toolkit for performing binary fixed-point arithmetic, including trigonometric and exponential functions.

The package provides:

  • Representations of values with a fixed number of fractional bits
  • Optional constraints on the number of whole-number bits
  • Interconversion between native Python types and fixed-point objects
  • Arithmetic operations (addition, subtraction, multiplication, division) of fixed-point numbers
  • Methods for computing powers, logarithms and exponents
  • Methods for computing trigonometric functions and their inverses
  • Computation of various mathematical constants, such as pi and log(2), to maximal precision for the chosen fixed-point resolution
  • Printing fixed-point numbers as decimal numbers, or as binary/octal/hexadecimal representations.
  • Support for numbers with thousands of bits of resolution

On a modern desktop PC, spfpm is typically capable of hundreds of thousands of arithmetic operations per second, i.e. over 100 kilo-FLOPS, even for a few hundred bits of resolution. As a pure-Python library SPFPM offers portability and interactivity, but will never compete with the performance of lower-level libraries such as mpmath, Boost multiprecision or GMP for heavyweight calculations.

Development currently targets Python versions 3.3 and later, although the library may also be usable with python-2.7. The latest version of spfpm can be found on GitHub.

Examples

After installation there are two main classes that you need from the FixedPoint module:

from FixedPoint import FXfamily, FXnum

you can create fixed-point numbers with the default (64-bit) resolution as follows:

x = FXnum(22) / FXnum(7)
y = FXnum(3.1415)
print(x - y)

Creating numbers with a specific precision requires use of the FXfamily class:

fam100 = FXfamily(100)
z = FXnum(1, fam100)
z2 = fam100(2)

One can then apply various computations such as:

print(z.atan() * 4)
print(z2.sqrt())

The FXfamily class also provides access to pre-computed constants which should be accurate to about 1/2 of the least significant bit (LSB):

print('pi = ', fam100.pi)
print('log2 = ', fam100.log2)
print('sqrt2 = ', fam100.sqrt2)

This produces the following printed values of those constants:

  • 3.141592653589793238462643383279
  • 0.69314718055994530941723212145 7
  • 1.414213562373095048801688724209

The struck-through digits show where the values computed at 100-bit precision differ from the true digits in the decimal representations of these constants. Alternatively, one could print these values in base-16:

print(FXfamily(400).pi.toBinaryString(logBase=4))

giving a hexadecimal value of Pi as:

  • 3.243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89452821e638d01377be5466cf34e90c6cc0ac

which agrees exactly with the accepted result.

Licensing

All files are released under the Python PSF License and are Copyright 2006-2024 RW Penney.

spfpm's People

Contributors

0xflotus avatar rwpenney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

spfpm's Issues

Invalid? representation

Hi

Lets say that I want to represent 0.75 (0 * 2 ^0 + 1 * 2 ^-1 + 1 * 2 ^-1) . In theory I only need 2 bits before the binary dot (one for the sing and one for the 0/1) and two bits after the dot.

But this is whay I got:

In [61]: print(FixedPoint.FXnum(0.75, FixedPoint.FXfamily(2,2)))
0.

If I increase the number of bits after the dot:

In [62]: print(FixedPoint.FXnum(0.75, FixedPoint.FXfamily(3,2)))
0.7

I get 0.7, which is not representable in a binary way :S with only 3 bits

I get the right output 0.75 with 6 bits:

In [65]: print(FixedPoint.FXnum(0.75, FixedPoint.FXfamily(6,2)))
0.75

Is this a a bug or I am not understanding the library?

Thanks!

Feature Request: displaying as bit pattern

Hi there,

I think it would be a good idea to have a function that returns a string of the bit representation of the fixed point number.
That was the library can be used to generate fixed point bit patterns for hardware designs.

toDecimalString not showing enough decimals

The following code

from FixedPoint import FXfamily, FXnum
fam2 = FXfamily(2)
z = FXnum(1.25, fam2)
print(z.toDecimalString())
print(z.toBinaryString())

prints

1.2
1.01

The binary value (1.01) is clearly correct and correctly printed. However, the decimal value printed is 1.2, when I believe the desired result would be for it to print 1.25 if no precision is set in the toDecimalString() method.

The string 1.25 is printed if print(z.toDecimalString(2)) is used (or any number >2).

I believe the issue comes from how precision is set in toDecimalString() in the default case. See line 487 of FixedPoint.py (commit c067b92). It is not large enough for the case of a fractional precision of 2. I have not tested it with other values of the fractional precision.

numpy array support

Hi, nice to see your effort of offering such library.
Could you offer numpy array support? As for signal processing, we usually do with numpy arrays, not just signle value.

toBinaryString() fails with n_intbits less than 2

toBinaryString() fails in that case because the conversion of 1 to a FXnum fails in rmul line 425 (called from line 515). Would be nice to be able to use such numbers for hardware-related tasks.

0**x returns unity

I do believe this is an error and can be fixed by changing line 561

return self.family.unity -> return self.family.zero

I dont know if this will result in any unforeseen issues.

Wrong return value of toDecimalString()

>>> FXnum(+0.5, family = FXfamily(n_bits=1,n_intbits=3)).toBinaryString()
'000.1'
FXnum(+0.5, family = FXfamily(n_bits=1,n_intbits=3)).toDecimalString()
'0.'

The decimal string should be 0.5

Mismatched Families Exception

Hello,

I found this problem in your very nice library:

a = FXnum(1.2, FXfamily(8))
b = FXnum(1.8, FXfamily(8))
c = a*b # throws exception!

But if you write it like this, it works:

fam = FXfamily(8)
a = FXnum(1.2, fam)
b = FXnum(1.8, fam)
c = a*b

Thank you and Greetings,
Ralf

Division Bug with Negative Divisor

Hello,

I've recently discovered your module and find it useful in hardware modelling. Thank you.

I think I've found a bug when dividing by a negative number. The answer seems to be off by one bit.

Dividing a negative number by a positive number produces the expected result.

I'm running SPFPM 1.6 on macOS 12.6.2 (arm64) with Python 3.10.9.

I've created a test script to show what I'm seeing:

from FixedPoint import FXfamily, FXnum

fp_family = FXfamily(n_bits=4, n_intbits=4)  # happens for other precisions too

x = fp_family(-1.0)
y = fp_family(1.0)
z = x / y
print("-1.0/1.0  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(1.0)
y = fp_family(-1.0)
z = x / y
print("1.0/-1.0  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(-1.0)
y = fp_family(-1.0)
z = x / y
print("-1.0/-1.0 ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(-3.0)
y = fp_family(2.0)
z = x / y
print("-3.0/2.0  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(3.0)
y = fp_family(-2.0)
z = x / y
print("3.0/-2.0  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(-3.0)
y = fp_family(-2.0)
z = x / y
print("-3.0/-2.0 ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(-7.5)
y = fp_family(2.5)
z = x / y
print("-7.5/2.5  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(7.5)
y = fp_family(-2.5)
z = x / y
print("7.5/-2.5  ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

x = fp_family(-7.5)
y = fp_family(-2.5)
z = x / y
print("-7.5/-2.5 ", z.toBinaryString(), " ", end="")
print(z.toDecimalString(precision=fp_family.fraction_bits))

Output

$ python3 spfpm-test.py
-1.0/1.0   1111.0000  -1
1.0/-1.0   1110.1111  -1.0625
-1.0/-1.0  0000.1111  0.9375
-3.0/2.0   1110.1000  -1.5
3.0/-2.0   1110.0111  -1.5625
-3.0/-2.0  0001.0111  1.4375
-7.5/2.5   1101.0000  -3
7.5/-2.5   1100.1111  -3.0625
-7.5/-2.5  0010.1111  2.9375

Rounding seems erroneous

The fixed point scaling from this library seems wrong

For example, for a family object of FXFamily(4,8), the fixed point scaled value for 1.0525 is calculated to be 1.0. It should be 1.0625.
This is probably because scaledval does not seem to rounded properly (always takes the floor.)

scaled value behaviour

Hello,

I don't understand how the scaled value works. At first I thought it was the "raw" value (the unsigned value without interpreting the FixedPoint format), but I'm having issues with negative values. Here is the code to reproduce what I'm saying.

fp = FXfamily(8,8)
a = fp(-1)
a_string = a.toBinaryString()

So now a_string is equal to '111111111.00000000'.

a_int = int(a_string.replace(".",""), 2) # Convert from base 2 to 10
b = fp(1)
b.scaledval = a_int

However now b is equal to 255 with a scaled value of 65280.
Am I not understanding correctly what the scaled value is?

This method works with unsigned numbers, but not with signed ones.
I'm using spfpm 1.4.

Thank you very much for providing this library in the first place, it is very useful to me :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.