From d385f2b6da1d69f383416e19ab622975b994964a Mon Sep 17 00:00:00 2001 From: Florian Schanda Date: Thu, 14 Mar 2019 13:41:52 +0100 Subject: [PATCH] Add lots of documentation. --- docs/.doctrees/environment.pickle | Bin 3635 -> 194818 bytes docs/.doctrees/index.doctree | Bin 7359 -> 142561 bytes docs/_sources/index.txt | 9 + docs/genindex.html | 482 +++++++++++++++++++- docs/index.html | 715 +++++++++++++++++++++++++++++- docs/objects.inv | Bin 268 -> 766 bytes docs/py-modindex.html | 5 + docs/searchindex.js | 2 +- index.rst | 9 + mpf/floats.py | 279 ++++++++++-- mpf/rationals.py | 70 ++- 11 files changed, 1529 insertions(+), 42 deletions(-) diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle index 2cb72334d6c27c6716ba1bf92fe0881bd5d7d4cf..bafb806b8281d9faee9e819ef6e58f333833c394 100644 GIT binary patch literal 194818 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 literal 3635 zcma)9U2hx56;&jeBB>ACa+H_4DVwAP)kdU~C{^4yCzf49u%b#%gQgKSv%5pi$otiq zSxW?Lw0*E@0|O*Lru_x|0sS}m9Zg@;qUX*oDatkq381)p_s;CxbI(2Z^3U^s>#ofB zKf9_6A+wVn&mKvYXDQE&vsXSYWfE^snZ9-Fe{oM-*DfkP2%e7o z{Z%k zBmkh|8PA{I}T12kD-=DlrX05Xlp7+LmI+IxkhkCthX9#D{hUFEwmPdpmKx z47FXT@pe1!+ZALm7yw_NQ4dfEVHY>7RwZx;`|p|i(^96sLri?Mez&Ex7MIPhp=a#ycix}V2o!aL_)71oPO zfv9bpWqEcM8v;T|r}TOp0N-B;M&D&=XODdJgbuAN?Q78(t?GU*Dhh9ThwxK*ZUQ|@ z!#qKO)eu#=&^_|Bjf3jx8BHbX!N`H+%I%7GL9xwUyMUy6lI+?gDq6tGZ>gb1AuPrm zv{fEw+^&ot@4=SBp$w~o&7z3vymswX6j!eMAK1DH~xzMu)UeBdqkoj)DT>jg8fs$jP+>w^UCR<@|h%j4wCHbEd6VG;YH*mC%^o%+r7R0@n?4*-`lEq9D?eE^(z5953fB)0{#|PWL+uq+^@BiV(#^&Ld>u#rW>h`h0nL?JKU6EN)ngH@j z1u}%-mu9f>(T0||zQWNaVw`vc2^E|hM79<|ZyyF6T9FVb6!&uY%r2=xGzH!DF?1%+ zA-Iy8frn0*Gy!ZshR&wJ0Nl@V!x7#9gvcTu=z>84(L8DisQL_&`^>flOH7dPM?7&m zm+TwFia=(XDRDsmg648qCVx;6y zpslXGf8)KI?{@AaxsojF97KXaBMF<6C@Ete6zCaTLHrJ(c#WXyoVN8qK#_GX4gqQJ z_Cau9r~`6)3A`(b)~hCua~!|gg>E#(h(^IP>ao>^V?ltq$4c_TIj4<8G!CV>E?ALuo`A7H~#~Y%yq_hX`k1P(0x%4j}bj??&$f z!q>N~K$nAoJfQ|T-=n_4#7*(tnb2rLNzmhVFcu%}n7u-50KcG2(Zpq;HsJY6F&ffz zqbm-Q>ifwDEfH;py3(Xs+7?Bqpo~<};0o<*1V`7bo3;dU&4gvcl5l|N*(>$Io$y8iOi)Ax1liTTpzb^-iSG+R=rjZ{_aa{6UuDR}rucUSpZEd&`#%@Lu7#~Gwl=YHfgWa@=Y{JkC( znkhG|9(u*v6VyIZjgfWCiYMSYiWkzZ(I07Sy>=FEr&}Sgj&}YEEVg!y>mD8B#P%oE z2-RKhbY<45d^=|wtvm5lkUPh3m`IW!*7NjAJ?vB8Aw_=T3(_}@n0`=QrXK*HE!tm2 NV> diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree index 624c77004097d698de280ae6db55e45630b7c636..c7a170b8d6ed16fe1986c64ceba84be6f8c8d598 100644 GIT binary patch literal 142561 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 QyP^YcN)vL>qFJ}|KPv+gO8@`> diff --git a/docs/_sources/index.txt b/docs/_sources/index.txt index f4d8499..beabcd0 100644 --- a/docs/_sources/index.txt +++ b/docs/_sources/index.txt @@ -5,9 +5,18 @@ PyMPF .. toctree:: :maxdepth: 2 +========= +Rationals +========= + +.. automodule:: mpf.rationals + :members: + :special-members: + === MPF === .. automodule:: mpf.floats :members: + :special-members: diff --git a/docs/genindex.html b/docs/genindex.html index f79430f..ffcf8c3 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -52,10 +52,142 @@

Navigation

Index

+

_

+ + + +
+ +
__abs__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__add__() (mpf.rationals.Rational method) +
+ + +
__eq__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__ge__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__gt__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__le__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+
+ +
__lt__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__mul__() (mpf.rationals.Rational method) +
+ + +
__ne__() (mpf.rationals.Rational method) +
+ + +
__neg__() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
__pow__() (mpf.rationals.Rational method) +
+ + +
__sub__() (mpf.rationals.Rational method) +
+ + +
__truediv__() (mpf.rationals.Rational method) +
+ + +
__weakref__ (mpf.floats.MPF attribute) +
+ +
+ +
(mpf.rationals.Rational attribute) +
+ +
+
+ +

C

+ + +
+ +
compatible() (mpf.floats.MPF method) +
+ +
+

F

+ +
@@ -63,6 +195,146 @@

F

fp_add() (in module mpf.floats)
+ +
fp_div() (in module mpf.floats) +
+ + +
fp_fma() (in module mpf.floats) +
+ + +
fp_from_float() (in module mpf.floats) +
+ + +
fp_from_int() (in module mpf.floats) +
+ + +
fp_from_sbv() (in module mpf.floats) +
+ + +
fp_from_ubv() (in module mpf.floats) +
+ + +
fp_max() (in module mpf.floats) +
+ + +
fp_min() (in module mpf.floats) +
+ + +
fp_mul() (in module mpf.floats) +
+ +
+ +
fp_nextDown() (in module mpf.floats) +
+ + +
fp_nextUp() (in module mpf.floats) +
+ + +
fp_rem() (in module mpf.floats) +
+ + +
fp_roundToIntegral() (in module mpf.floats) +
+ + +
fp_sqrt() (in module mpf.floats) +
+ + +
fp_sub() (in module mpf.floats) +
+ + +
fp_to_int() (in module mpf.floats) +
+ + +
fp_to_sbv() (in module mpf.floats) +
+ + +
fp_to_ubv() (in module mpf.floats) +
+ + +
from_rational() (mpf.floats.MPF method) +
+ +
+ +

I

+ + +
+ +
isFinite() (mpf.floats.MPF method) +
+ + +
isInfinite() (mpf.floats.MPF method) +
+ + +
isIntegral() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+ +
isNaN() (mpf.floats.MPF method) +
+ + +
isNegative() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+
+ +
isNormal() (mpf.floats.MPF method) +
+ + +
isPositive() (mpf.floats.MPF method) +
+ + +
isSubnormal() (mpf.floats.MPF method) +
+ + +
isZero() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
@@ -70,9 +342,217 @@

M

+ +
+
MPF (class in mpf.floats) +
+ +
+
mpf.floats (module)
+ +
mpf.rationals (module) +
+ +
+ +

N

+ + +
+ +
new_mpf() (mpf.floats.MPF method) +
+ +
+ +

P

+ + +
+ +
pack() (mpf.floats.MPF method) +
+ +
+ +

Q

+ + + +
+ +
q_from_decimal_fragments() (in module mpf.rationals) +
+ + +
q_pow2() (in module mpf.rationals) +
+ + +
q_round_rna() (in module mpf.rationals) +
+ + +
q_round_rne() (in module mpf.rationals) +
+ +
+ +
q_round_rtn() (in module mpf.rationals) +
+ + +
q_round_rtp() (in module mpf.rationals) +
+ + +
q_round_rtz() (in module mpf.rationals) +
+ + +
q_round_to_nearest() (in module mpf.rationals) +
+ +
+ +

R

+ + +
+ +
Rational (class in mpf.rationals) +
+ +
+ +

S

+ + + +
+ +
set_infinite() (mpf.floats.MPF method) +
+ + +
set_nan() (mpf.floats.MPF method) +
+ + +
set_sign_bit() (mpf.floats.MPF method) +
+ + +
set_zero() (mpf.floats.MPF method) +
+ + +
smtlib_eq() (in module mpf.floats) +
+ + +
smtlib_from_binary_interchange() (mpf.floats.MPF method) +
+ + +
smtlib_from_float() (mpf.floats.MPF method) +
+ + +
smtlib_from_int() (mpf.floats.MPF method) +
+ +
+ +
smtlib_from_real() (mpf.floats.MPF method) +
+ + +
smtlib_from_sbv() (mpf.floats.MPF method) +
+ + +
smtlib_from_ubv() (mpf.floats.MPF method) +
+ + +
smtlib_literal() (mpf.floats.MPF method) +
+ + +
smtlib_literals() (mpf.floats.MPF method) +
+ + +
smtlib_random_literal() (mpf.floats.MPF method) +
+ + +
smtlib_sort() (mpf.floats.MPF method) +
+ + +
smtlib_to_int() (mpf.floats.MPF method) +
+ + +
smtlib_to_real() (mpf.floats.MPF method) +
+ +
+ +

T

+ + + +
+ +
to_decimal_string() (mpf.rationals.Rational method) +
+ + +
to_int() (mpf.floats.MPF method) +
+ + +
to_python_float() (mpf.floats.MPF method) +
+ +
+ +
(mpf.rationals.Rational method) +
+ +
+
+ +
to_python_int() (mpf.rationals.Rational method) +
+ + +
to_python_string() (mpf.floats.MPF method) +
+ + +
to_rational() (mpf.floats.MPF method) +
+ + +
to_smtlib() (mpf.rationals.Rational method) +
+ +
+ +

U

+ +
+ +
unpack() (mpf.floats.MPF method) +
+
diff --git a/docs/index.html b/docs/index.html index 17644be..3f9e6f5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -51,33 +51,727 @@

Navigation

PyMPF

+ +
+

Rationals

+

This module defines class to deal with Rational numbers.

+
+

Todo

+

This should be a subclass of fractions.Fraction.

+
+
+
+class mpf.rationals.Rational(a=0, b=1)
+

Rational number

+

a is the numerator

+

b is the denominator

+
+
+__abs__()
+

Absolute value

+
+ +
+
+__add__(other)
+

Addition

+
+ +
+
+__eq__(other)
+

Equality

+
+ +
+
+__ge__(other)
+

>=

+
+ +
+
+__gt__(other)
+

>

+
+ +
+
+__le__(other)
+

<=

+
+ +
+
+__lt__(other)
+

<

+
+ +
+
+__mul__(other)
+

Multiplication

+
+ +
+
+__ne__(other)
+

Inequality

+
+ +
+
+__neg__()
+

Negation

+
+ +
+
+__pow__(other)
+

Exponentiation

+

The right-hand side must be an integral Rational, otherwise +AssertionError is raised.

+
+ +
+
+__sub__(other)
+

Substraction

+
+ +
+
+__truediv__(other)
+

Division

+
+ +
+
+__weakref__
+

list of weak references to the object (if defined)

+
+ +
+
+isIntegral()
+

Test if integral

+
+ +
+
+isNegative()
+

Test if negative

+

Returns false for 0.

+
+ +
+
+isZero()
+

Test if zero

+
+ +
+
+to_decimal_string()
+

Convert to decimal string

+

If the fraction does not terminate (e.g. for 1 / 3), an +exception is thrown.

+
+ +
+
+to_python_float()
+

Convert to python float

+
+ +
+
+to_python_int()
+

Convert to python int

+
+ +
+
+to_smtlib()
+

Convert to SMT-LIB Real expression

+

Returns literal for integrals, e.g. “1.0”.

+

Returns an s-expression otherwise, e.g. “(- (/ 1.0 3.0))”.

+
+ +
+ +
+
+mpf.rationals.q_from_decimal_fragments(sign, integer_part, fraction_part, exp_part)
+

Build a rational from string fragments of a decimal number.

+
+
E.g. for “1.23E-1” we have fragments for
+
sign = “” +integer_part = “1” +fraction_part = “23” +exp_part = “-1”
+
+
+ +
+
+mpf.rationals.q_pow2(n)
+

Create rational for 2^n

+

n can be negative

+
+ +
+
+mpf.rationals.q_round_rna(n)
+

Round to nearest integer, away from zero

+
+ +
+
+mpf.rationals.q_round_rne(n)
+

Round to nearest even integer

+
+ +
+
+mpf.rationals.q_round_rtn(n)
+

Round to nearest integer, towards negative

+
+ +
+
+mpf.rationals.q_round_rtp(n)
+

Round to nearest integer, towards positive

+
+ +
+
+mpf.rationals.q_round_rtz(n)
+

Round to nearest integer, towards zero

+
+ +
+
+mpf.rationals.q_round_to_nearest(n, tiebreak)
+

Round to nearest integer

+

Round n to the nearest integer

+

Calls the tiebreak*(upper, lower) function with the two +alternatives if *n is precisely between two integers.

+
+

MPF

+

This module implements IEEE floats using bitvectors as the in-memory +format, but rationals (plus rounding and special case handling) for +calculation. This allows us to directly implement the semantics +described in IEEE-754 (2008) without having to consider any of the +difficult normalisation problems.

+

The main objective is that the implementation should be simple and +simple to understand and as close to IEEE-754 and SMTLIB as possible +(as opposed to fast) since the main use is the validation of other +IEEE float implementations in SMT solvers (which have to be +fast). It is also not a replacement for libraries such as MPFR +(which is fast, but complicated and does not totally map onto IEEE +floats).

+
+
+class mpf.floats.MPF(eb, sb, bitvec=0)
+

Arbitrary precision IEEE-754 floating point number

+

eb is the number of bits for the exponent.

+

sb is the number of bits for the significand.

+

This includes the “hidden bit”, so for example to create a +single-precision floating point number we use:

+
>>> x = MPF(8, 24)
+
+
+

By default the created float is +0, however bitvec can be used +to set the initial value. The expected format is an integer +\(0 \le bitvec < 2^{eb+sb}\) corresponding to the binary +interchange format. For example to create the single precision +float representing +1:

+
>>> x = MPF(8, 24, 0x3f800000)
+>>> x.to_python_string()
+'1.0'
+
+
+
+
+__abs__()
+

Compute absolute value

+
+ +
+
+__eq__(other)
+

Test floating-point equality

+

Note that this is floating-point equality, so comparisons to +NaN always fail and -0 and +0 are considered equal.

+

If you want true equality, use smtlib_eq()

+
+ +
+
+__ge__(other)
+

Test floating-point greater than or equal

+
+ +
+
+__gt__(other)
+

Test floating-point greater than

+
+ +
+
+__le__(other)
+

Test floating-point less than or equal

+
+ +
+
+__lt__(other)
+

Test floating-point less than

+
+ +
+
+__neg__()
+

Compute inverse

+
+ +
+
+__weakref__
+

list of weak references to the object (if defined)

+
+ +
+
+compatible(other)
+

Test if another MPF has the same precision

+
+ +
+
+from_rational(rm, q)
+

Convert from rational to MPF

+

Sets the value to the nearest representable floating-point +value described by q, rounded according to rm.

+
+ +
+
+isFinite()
+

Test if value is finite

+

Returns true for zero, subnormals, or normal numbers.

+

Returns false for infinities, and not a number.

+
+ +
+
+isInfinite()
+

Test if value is infinite

+
+ +
+
+isIntegral()
+

Test if value is integral

+

Returns true if the floating-point value is an integer, and +false in all other cases (including infinities and not a +number).

+
+ +
+
+isNaN()
+

Test if value is not a number

+
+ +
+
+isNegative()
+

Test if value is negative

+

Returns always false for NaN.

+
+ +
+
+isNormal()
+

Test if value is normal

+
+ +
+
+isPositive()
+

Test if value is positive

+

Returns always false for NaN.

+
+ +
+
+isSubnormal()
+

Test if value is subnormal

+
+ +
+
+isZero()
+

Test if value is zero

+
+ +
+
+new_mpf()
+

Copy constructor

+

Returns a new MPF with the same precision and value.

+
+ +
+
+pack(S, E, T)
+

Pack sign, exponent, and significand

+

Sets the value of the floating point number, such that

+

The sign corresponds to S, the exponent is E, and the +significand is T.

+

This is the inverse of unpack().

+
+ +
+
+set_infinite(sign)
+

Set value to infinite with the given sign bit

+
+ +
+
+set_nan()
+

Set value to NaN

+
+ +
+
+set_sign_bit(sign)
+

Set sign bit

+
+ +
+
+set_zero(sign)
+

Set value to zero with the given sign bit

+
+ +
+
+smtlib_from_binary_interchange()
+

SMT-LIB function for converting from bitvector

+
+ +
+
+smtlib_from_float()
+

SMT-LIB function for converting from FloatingPoint

+
+ +
+
+smtlib_from_int()
+

SMT-LIB function for converting from Int

+
+ +
+
+smtlib_from_real()
+

SMT-LIB function for converting from Real

+
+ +
+
+smtlib_from_sbv()
+

SMT-LIB function for converting from signed bitvector

+
+ +
+
+smtlib_from_ubv()
+

SMT-LIB function for converting from unsigned bitvector

+
+ +
+
+smtlib_literal()
+

Return an SMT-LIB literal

+

Favours special cases, such as (_ +zero 8 24), otherwise +returns literals of the form (fp ...).

+
+ +
+
+smtlib_literals()
+

Return list of all possible SMTLIB literals

+

Includes:

+
    +
  • Binary interchange conversion, e.g. ((_ to_fp 8 24) #x00000000)
  • +
  • FP literal, e.g. (fp #b0 #b00 #b000)
  • +
  • Special value, e.g. (_ +zero 2 4)
  • +
+
+ +
+
+smtlib_random_literal()
+

Return an SMT-LIB literal (randomly chosen)

+

Chooses randomly from smtlib_literals().

+
+ +
+
+smtlib_sort()
+

SMT-LIB sort

+

Returns “Float16”, “Float32”, “Float64”, or “Float128” if +possible.

+

Returns “(_ FloatingPoint eb sb)” otherwise.

+
+ +
+
+smtlib_to_int()
+

SMT-LIB function for converting to Int

+
+ +
+
+smtlib_to_real()
+

SMT-LIB function for converting to Real

+
+ +
+
+to_int(rm)
+

Convert from MPF to Python int`

+

Raises AssertionError for infinities and NaN. Returns the +integer closest to the floating point, rounded according to +rm.

+
+ +
+
+to_python_float()
+

Convert from MPF to Python float`

+

Convert to python float. Do not rely on this to be precise for +now.

+
+

Todo

+

Use sys.float_info to first convert to the correct +format and then directly interpret the bit-pattern.

+
+

If you want something precise, then use +to_python_string() instead.

+
+ +
+
+to_python_string()
+

Convert from MPF to Python string`

+

Return a string describing the float. We return a decimal +string (e.g. ‘0.25’), except for the following special cases: +‘-0’, ‘Infinity’, ‘-Infinity’, and ‘NaN’.

+

The decimal string might be very long (but precise), so if you +want something compact then to_python_float() might be +more appropriate.

+
+ +
+
+to_rational()
+

Convert from MPF to Rational

+

Raises AssertionError for infinities or NaN.

+
+ +
+
+unpack()
+

Unpack into sign, exponent, and significand

+

Returns a tuple of integers (S, E, T) where S is the sign +bit, E is the exponent, and T is the significand.

+

This is the inverse of pack().

+
+ +
+
mpf.floats.fp_add(rm, left, right)

Floating-point addition

Performs a correctly rounded \(left + right\). The following special cases apply:

-
    -
  • NaN propagates

    -
  • -
  • Adding opposite infinities results in NaN, otherwise infinities propagate

    -
  • -
  • If the precise result is exactly 0 then we special case -according to Section 6.3 in IEEE-754:

    -
    -
      +
        +
      • NaN propagates
      • +
      • Adding opposite infinities results in NaN, otherwise infinities propagate
      • +
      • If the precise result is exactly 0 (i.e. 0 + 0 or a + -a) then we +special case according to Section 6.3 in IEEE-754:
        • we preserve the sign if left and right have the same sign
        • otherwise, we return -0 if the rounding mode is RTN
        • otherwise, we return +0
        -
+
+
+mpf.floats.fp_div(rm, left, right)
+

Floating-point division

+

Performs a correctly rounded \(left \div right\). The +following special cases apply:

+
    +
  • NaN propagates
  • +
  • When dividing by infinity
      +
    • NaN when dividing two infinities
    • +
    • 0 otherwise
    • +
    +
  • +
  • When dividing by zero
      +
    • NaN when dividing zero by zero
    • +
    • Infinity otherwise
    • +
    +
  • +
+
+ +
+
+mpf.floats.fp_fma(rm, x, y, z)
+

Floating-point fused multiply add

+

Performs a correctly rounded \(x * y + z\). The special cases +of fp_mul() and fp_add() apply, and in addition:

+
    +
  • On a precise 0 result the sign of zero is:
      +
    • Preserved if the sign of x * y is the same as z
    • +
    • -0 for RTN
    • +
    • +0 otherwise
    • +
    +
  • +
+
+ +
+
+mpf.floats.fp_from_float(eb, sb, rm, op)
+

Conversion from MPF to MPF (of a different precision)

+
+ +
+
+mpf.floats.fp_from_int(eb, sb, rm, op)
+

Conversion from Python integer to MPF

+
+ +
+
+mpf.floats.fp_from_sbv(eb, sb, rm, op)
+

Conversion from signed bitvector to MPF

+
+ +
+
+mpf.floats.fp_from_ubv(eb, sb, rm, op)
+

Conversion from unsigned bitvector to MPF

+
+ +
+
+mpf.floats.fp_max(left, right)
+

Floating-point maximum

+
+ +
+
+mpf.floats.fp_min(left, right)
+

Floating-point minimum

+
+ +
+
+mpf.floats.fp_mul(rm, left, right)
+

Floating-point multiplication

+

Performs a correctly rounded \(left * right\). The following +special cases apply:

+
    +
  • NaN propagates
  • +
  • Infinities propagate (for non-zero operands)
  • +
  • Multiplying by zero gives the following results
      +
    • NaN if the other operand is infinite
    • +
    • 0 of the same sign as the 0 operand
    • +
    +
  • +
+
+ +
+
+mpf.floats.fp_nextDown(op)
+

Floating-point predecessor

+
+ +
+
+mpf.floats.fp_nextUp(op)
+

Floating-point successor

+
+ +
+
+mpf.floats.fp_rem(left, right)
+

Floating-point remainder

+
+ +
+
+mpf.floats.fp_roundToIntegral(rm, op)
+

Floating-point round to integer

+
+ +
+
+mpf.floats.fp_sqrt(rm, op)
+

Floating-point square root

+
+ +
+
+mpf.floats.fp_sub(rm, left, right)
+

Floating-point substraction

+

Performs a correctly rounded \(left - right\), which is +equivalent to \(left + -right\).

+

See fp_add() for special cases.

+
+ +
+
+mpf.floats.fp_to_int(rm, op)
+

Conversion from MPF to Python integer

+
+ +
+
+mpf.floats.fp_to_sbv(op, rm, width)
+

Conversion from MPF to signed bitvector

+
+ +
+
+mpf.floats.fp_to_ubv(op, rm, width)
+

Conversion from MPF to unsigned bitvector

+
+ +
+
+mpf.floats.smtlib_eq(left, right)
+

Bit-wise equality

+
+
@@ -89,6 +783,7 @@

PyMPF

Table Of Contents

diff --git a/docs/objects.inv b/docs/objects.inv index c18ab1c7097797cbb9b62bfe2ac54da8e4b9e2f9..bd25c1bbfb2f964fadb17d6fc5867d3924e78c0f 100644 GIT binary patch delta 658 zcmV;D0&V?_0{#V%cz>OkO>f&U42JLd6$Z9zf_2B9hXOsc2-fYgiy>RIBUGXsNzO-q z{b)OBQq*Wf-U9i3NZF)FF|KP?jrA(UqC4I@uX_u{kHVma!)lY9{Wh&FzZc7edl#>s z)(-{T$QD&Vp)zJAZ@(PAYjV`THpeS30STN%YXHN&@zcVsMi3^M5&a5 zsrm#r!P>h{B~wwb^Sw2}yD|{wQCS9O;)6kxfQ39_BY_gMfw{S7^~}>lOW$C}+u5@Z zj=8V3kB05%rLU0h>K_Ep01);iEis5r)vpYBgj5=S0t^@`WD?P|SXL%6u{Yi~i!NDH zmE#$z461OXH-AhJ>W#t{goegtAxqwcWB^urU8Awu3CUhUTFRdw+3X37d@?B1v|X|1 z=z}0G$;;wN95b41PXu&FQX)HBFJz>?hAj^jG^!pMb*kw~rS%|gdaMUux8&OFIP%6R zmi$9OU{R-lvY69JW-&dFaq7U58)V6Q%aJ1-(l5V9E`Rg4;}8KHOYVErfBf~eCl`&| zzzTZoD~|k9CKv=Wr-V&l8JK23 zs#qFoLx1C0=9OmN2IoHYrG<=}+&n+k(tLJ1T>#E6-vre8_+RDgJ+N4J3EN)eO3x2ghLEbof*>ZmIoho6g!O#6BeT`AT_ sx?V4+Y0lW{j?43dN&GbR+sSIAP1c)_#I!bvUkj$^Yt_QyKce14ipX<9k^lez delta 156 zcmV;N0Av6D1&jiaczg!3;DyOU~aGJP_5u@ zc=NuG8K11ME~=6t)!s+5ID`|-!QfL&#k&JBegw|^M;12Slrb%1m!QiW19baT(?<*| zfm}@Q)Eyo04mP@aaA_m^Y9{I??F?B5d1mh`{&c0vw*|=qJrzK$8P!8izX@U_UuiGZ Ki0lQ!nOn-ABSzQ& diff --git a/docs/py-modindex.html b/docs/py-modindex.html index 2532f57..0e236d5 100644 --- a/docs/py-modindex.html +++ b/docs/py-modindex.html @@ -72,6 +72,11 @@

Python Module Index

    mpf.floats + + +     + mpf.rationals + diff --git a/docs/searchindex.js b/docs/searchindex.js index f599677..9c5711d 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({envversion:50,filenames:["index"],objects:{"mpf.floats":{fp_add:[0,1,1,""]},mpf:{floats:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:function"},terms:{"case":0,"float":0,"return":0,accord:0,adding:0,addit:0,appli:0,correctli:0,exactli:0,follow:0,fp_add:0,have:0,ieee:0,infin:0,left:0,mode:0,nan:0,opposit:0,otherwis:0,perform:0,point:0,precis:0,preserv:0,propag:0,result:0,right:0,round:0,rtn:0,same:0,section:0,sign:0,special:0},titles:["PyMPF"],titleterms:{mpf:0,pympf:0}}) \ No newline at end of file +Search.setIndex({envversion:50,filenames:["index"],objects:{"mpf.floats":{MPF:[0,1,1,""],fp_add:[0,4,1,""],fp_div:[0,4,1,""],fp_fma:[0,4,1,""],fp_from_float:[0,4,1,""],fp_from_int:[0,4,1,""],fp_from_sbv:[0,4,1,""],fp_from_ubv:[0,4,1,""],fp_max:[0,4,1,""],fp_min:[0,4,1,""],fp_mul:[0,4,1,""],fp_nextDown:[0,4,1,""],fp_nextUp:[0,4,1,""],fp_rem:[0,4,1,""],fp_roundToIntegral:[0,4,1,""],fp_sqrt:[0,4,1,""],fp_sub:[0,4,1,""],fp_to_int:[0,4,1,""],fp_to_sbv:[0,4,1,""],fp_to_ubv:[0,4,1,""],smtlib_eq:[0,4,1,""]},"mpf.floats.MPF":{__abs__:[0,2,1,""],__eq__:[0,2,1,""],__ge__:[0,2,1,""],__gt__:[0,2,1,""],__le__:[0,2,1,""],__lt__:[0,2,1,""],__neg__:[0,2,1,""],__weakref__:[0,3,1,""],compatible:[0,2,1,""],from_rational:[0,2,1,""],isFinite:[0,2,1,""],isInfinite:[0,2,1,""],isIntegral:[0,2,1,""],isNaN:[0,2,1,""],isNegative:[0,2,1,""],isNormal:[0,2,1,""],isPositive:[0,2,1,""],isSubnormal:[0,2,1,""],isZero:[0,2,1,""],new_mpf:[0,2,1,""],pack:[0,2,1,""],set_infinite:[0,2,1,""],set_nan:[0,2,1,""],set_sign_bit:[0,2,1,""],set_zero:[0,2,1,""],smtlib_from_binary_interchange:[0,2,1,""],smtlib_from_float:[0,2,1,""],smtlib_from_int:[0,2,1,""],smtlib_from_real:[0,2,1,""],smtlib_from_sbv:[0,2,1,""],smtlib_from_ubv:[0,2,1,""],smtlib_literal:[0,2,1,""],smtlib_literals:[0,2,1,""],smtlib_random_literal:[0,2,1,""],smtlib_sort:[0,2,1,""],smtlib_to_int:[0,2,1,""],smtlib_to_real:[0,2,1,""],to_int:[0,2,1,""],to_python_float:[0,2,1,""],to_python_string:[0,2,1,""],to_rational:[0,2,1,""],unpack:[0,2,1,""]},"mpf.rationals":{Rational:[0,1,1,""],q_from_decimal_fragments:[0,4,1,""],q_pow2:[0,4,1,""],q_round_rna:[0,4,1,""],q_round_rne:[0,4,1,""],q_round_rtn:[0,4,1,""],q_round_rtp:[0,4,1,""],q_round_rtz:[0,4,1,""],q_round_to_nearest:[0,4,1,""]},"mpf.rationals.Rational":{__abs__:[0,2,1,""],__add__:[0,2,1,""],__eq__:[0,2,1,""],__ge__:[0,2,1,""],__gt__:[0,2,1,""],__le__:[0,2,1,""],__lt__:[0,2,1,""],__mul__:[0,2,1,""],__ne__:[0,2,1,""],__neg__:[0,2,1,""],__pow__:[0,2,1,""],__sub__:[0,2,1,""],__truediv__:[0,2,1,""],__weakref__:[0,3,1,""],isIntegral:[0,2,1,""],isNegative:[0,2,1,""],isZero:[0,2,1,""],to_decimal_string:[0,2,1,""],to_python_float:[0,2,1,""],to_python_int:[0,2,1,""],to_smtlib:[0,2,1,""]},mpf:{floats:[0,0,0,"-"],rationals:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:function"},terms:{"0x3f800000":0,"23e":0,"case":0,"class":0,"default":0,"float":0,"function":0,"int":0,"long":0,"new":0,"return":0,"true":0,__abs__:0,__add__:0,__eq__:0,__ge__:0,__gt__:0,__le__:0,__lt__:0,__mul__:0,__ne__:0,__neg__:0,__pow__:0,__sub__:0,__truediv__:0,__weakref__:0,absolut:0,accord:0,add:0,adding:0,addit:0,addition:0,all:0,allow:0,also:0,altern:0,alwai:0,ani:0,anoth:0,appli:0,appropri:0,arbitrari:0,assertionerror:0,awai:0,b000:0,b00:0,between:0,binari:0,bit:0,bitvec:0,bitvector:0,build:0,calcul:0,call:0,can:0,choos:0,chosen:0,close:0,closest:0,compact:0,comparison:0,compat:0,complic:0,comput:0,consid:0,constructor:0,convers:0,convert:0,copi:0,correct:0,correctli:0,correspond:0,creat:0,deal:0,decim:0,defin:0,denomin:0,describ:0,differ:0,difficult:0,directli:0,divid:0,divis:0,doe:0,equal:0,equaliti:0,equival:0,even:0,exactli:0,exampl:0,except:0,exp_part:0,expect:0,expon:0,exponenti:0,express:0,fail:0,fals:0,fast:0,favour:0,finit:0,first:0,float128:0,float16:0,float32:0,float64:0,float_info:0,floatingpoint:0,follow:0,form:0,format:0,fp_add:0,fp_div:0,fp_fma:0,fp_from_float:0,fp_from_int:0,fp_from_sbv:0,fp_from_ubv:0,fp_max:0,fp_min:0,fp_mul:0,fp_nextdown:0,fp_nextup:0,fp_rem:0,fp_roundtointegr:0,fp_sqrt:0,fp_sub:0,fp_to_int:0,fp_to_sbv:0,fp_to_ubv:0,fraction:0,fraction_part:0,fragment:0,from:0,from_rat:0,fuse:0,give:0,given:0,greater:0,hand:0,handl:0,have:0,hidden:0,howev:0,ieee:0,implement:0,includ:0,include:0,inequal:0,infin:0,infinit:0,infiniti:0,initi:0,instead:0,integ:0,integer_part:0,integr:0,interchang:0,interpret:0,invers:0,isfinit:0,isinfinit:0,isintegr:0,isnan:0,isneg:0,isnorm:0,isposit:0,issubnorm:0,iszero:0,left:0,less:0,lib:0,librari:0,list:0,liter:0,lower:0,main:0,map:0,maximum:0,memori:0,might:0,minimum:0,mode:0,modul:0,more:0,mpfr:0,multipl:0,multipli:0,must:0,nan:0,nearest:0,neg:0,negat:0,new_mpf:0,non:0,normal:0,normalis:0,note:0,now:0,number:0,numer:0,object:0,onto:0,operand:0,oppos:0,opposit:0,other:0,otherwis:0,pack:0,pattern:0,perform:0,plu:0,point:0,posit:0,possibl:0,precis:0,predecessor:0,preserv:0,problem:0,propag:0,python:0,q_from_decimal_frag:0,q_pow2:0,q_round_rn:0,q_round_rna:0,q_round_rtn:0,q_round_rtp:0,q_round_rtz:0,q_round_to_nearest:0,rais:0,randomli:0,real:0,refer:0,reli:0,remaind:0,replac:0,repres:0,represent:0,result:0,right:0,root:0,round:0,rtn:0,same:0,section:0,see:0,semant:0,set:0,set_infinit:0,set_nan:0,set_sign_bit:0,set_zero:0,should:0,side:0,sign:0,significand:0,simpl:0,sinc:0,singl:0,smt:0,smtlib:0,smtlib_eq:0,smtlib_from_binary_interchang:0,smtlib_from_float:0,smtlib_from_int:0,smtlib_from_r:0,smtlib_from_sbv:0,smtlib_from_ubv:0,smtlib_liter:0,smtlib_random_liter:0,smtlib_sort:0,smtlib_to_int:0,smtlib_to_r:0,solver:0,someth:0,sort:0,special:0,squar:0,string:0,subclass:0,subnorm:0,substract:0,successor:0,termin:0,test:0,than:0,thi:0,thrown:0,tiebreak:0,to_decimal_str:0,to_fp:0,to_int:0,to_python_float:0,to_python_int:0,to_python_str:0,to_rat:0,to_smtlib:0,total:0,toward:0,tupl:0,two:0,understand:0,unpack:0,unsign:0,upper:0,use:0,valid:0,valu:0,veri:0,want:0,weak:0,when:0,where:0,which:0,width:0,wise:0,without:0,x00000000:0,you:0,zero:0},titles:["PyMPF"],titleterms:{mpf:0,pympf:0,ration:0,todo:0}}) \ No newline at end of file diff --git a/index.rst b/index.rst index f4d8499..beabcd0 100644 --- a/index.rst +++ b/index.rst @@ -5,9 +5,18 @@ PyMPF .. toctree:: :maxdepth: 2 +========= +Rationals +========= + +.. automodule:: mpf.rationals + :members: + :special-members: + === MPF === .. automodule:: mpf.floats :members: + :special-members: diff --git a/mpf/floats.py b/mpf/floats.py index aec8ff9..c09f484 100644 --- a/mpf/floats.py +++ b/mpf/floats.py @@ -24,19 +24,21 @@ ## ## ############################################################################## -# This module implements IEEE floats using bitvectors as the in-memory -# format, but rationals (plus rounding and special case handling) for -# calculation. This allows us to directly implement the semantics -# described in IEEE-754 (2008) without having to consider any of the -# difficult normalisation problems. -# -# The main objective is that the implementation should be simple and -# simple to understand and as close to IEEE-754 and SMTLIB as possible -# (as opposed to fast) since the main use is the validation of other -# IEEE float implementations in SMT solvers (which have to be -# fast). It is also not a replacement for libraries such as MPFR -# (which is fast, but complicated and does not totally map onto IEEE -# floats). +""" +This module implements IEEE floats using bitvectors as the in-memory +format, but rationals (plus rounding and special case handling) for +calculation. This allows us to directly implement the semantics +described in IEEE-754 (2008) without having to consider any of the +difficult normalisation problems. + +The main objective is that the implementation should be simple and +simple to understand and as close to IEEE-754 and SMTLIB as possible +(as opposed to fast) since the main use is the validation of other +IEEE float implementations in SMT solvers (which have to be +fast). It is also not a replacement for libraries such as MPFR +(which is fast, but complicated and does not totally map onto IEEE +floats). +""" # TODO: Implement RNA in intervals @@ -63,7 +65,30 @@ class Unspecified(Exception): RM_RTN = "RTN" RM_RTZ = "RTZ" -class MPF(object): +class MPF: + """Arbitrary precision IEEE-754 floating point number + + *eb* is the number of bits for the exponent. + + *sb* is the number of bits for the significand. + + This includes the "hidden bit", so for example to create a + single-precision floating point number we use: + + >>> x = MPF(8, 24) + + By default the created float is +0, however *bitvec* can be used + to set the initial value. The expected format is an integer + :math:`0 \le bitvec < 2^{eb+sb}` corresponding to the binary + interchange format. For example to create the single precision + float representing +1: + + >>> x = MPF(8, 24, 0x3f800000) + >>> x.to_python_string() + '1.0' + + """ + ROUNDING_MODES = (RM_RNE, RM_RNA, RM_RTP, RM_RTN, RM_RTZ) ROUNDING_MODES_NEAREST = (RM_RNE, RM_RNA) ROUNDING_MODES_DIRECTED = (RM_RTP, RM_RTN, RM_RTZ) @@ -87,20 +112,37 @@ def __init__(self, eb, sb, bitvec=0): self.bv = bitvec + def __repr__(self): + return "MPF(%u, %u, 0x%x)" % (self.w, self.p, self.bv) + ###################################################################### # Constructors def new_mpf(self): + """Copy constructor + + Returns a new MPF with the same precision and value. + """ return MPF(self.w, self.p, self.bv) ###################################################################### # Internal utilities def compatible(self, other): + """Test if another MPF has the same precision""" return (self.w == other.w and self.p == other.p) def unpack(self): + """Unpack into sign, exponent, and significand + + Returns a tuple of integers (S, E, T) where *S* is the sign + bit, *E* is the exponent, and *T* is the significand. + + This is the inverse of :func:`pack`. + + """ + # TODO: remove ugly hack to convert via strings bits = "{0:b}".format(self.bv) assert len(bits) <= self.k @@ -113,6 +155,16 @@ def unpack(self): return (S, E, T) def pack(self, S, E, T): + """Pack sign, exponent, and significand + + Sets the value of the floating point number, such that + + The sign corresponds to *S*, the exponent is *E*, and the + significand is *T*. + + This is the inverse of :func:`unpack`. + + """ assert 0 <= S <= 1 assert 0 <= E <= 2 ** self.w - 1 assert 0 <= T <= 2 ** self.t - 1 @@ -142,6 +194,12 @@ def inf_boundary(self): return inf.to_python_int() def from_rational(self, rm, q): + """Convert from rational to MPF + + Sets the value to the nearest representable floating-point + value described by *q*, rounded according to *rm*. + + """ assert rm in MPF.ROUNDING_MODES inf = Rational(self.inf_boundary()) @@ -255,6 +313,10 @@ def from_rational(self, rm, q): self.set_sign_bit(sign) def to_rational(self): + """Convert from MPF to :class:`.Rational` + + Raises AssertionError for infinities or NaN. + """ S, E, T = self.unpack() if E == 2 ** self.w - 1: @@ -280,6 +342,13 @@ def to_rational(self): return v def to_int(self, rm): + """Convert from MPF to Python int` + + Raises AssertionError for infinities and NaN. Returns the + integer closest to the floating point, rounded according to + *rm*. + + """ assert rm in MPF.ROUNDING_MODES assert self.isFinite() @@ -289,6 +358,17 @@ def to_int(self, rm): return q.a def to_python_float(self): + """Convert from MPF to Python float` + + Convert to python float. Do not rely on this to be precise for + now. + + .. todo:: Use sys.float_info to first convert to the correct + format and then directly interpret the bit-pattern. + + If you want something precise, then use + :func:`to_python_string` instead. + """ if self.isNaN(): return float("NaN") elif self.isInfinite(): @@ -300,6 +380,17 @@ def to_python_float(self): return self.to_rational().to_python_float() def to_python_string(self): + """Convert from MPF to Python string` + + Return a string describing the float. We return a decimal + string (e.g. '0.25'), except for the following special cases: + '-0', 'Infinity', '-Infinity', and 'NaN'. + + The decimal string might be very long (but precise), so if you + want something compact then :func:`to_python_float` might be + more appropriate. + + """ if self.isNaN(): return "NaN" elif self.isInfinite(): @@ -316,19 +407,22 @@ def to_python_string(self): # Setters def set_zero(self, sign): + """Set value to zero with the given sign bit""" assert 0 <= sign <= 1 self.pack(sign, 0, 0) def set_infinite(self, sign): + """Set value to infinite with the given sign bit""" assert 0 <= sign <= 1 self.pack(sign, 2 ** self.w - 1, 0) def set_nan(self): + """Set value to NaN""" self.pack(1, 2 ** self.w - 1, 2 ** self.t - 1) def set_sign_bit(self, sign): - assert type(sign) is bool - + """Set sign bit""" + assert 0 <= sign <= 1 S, E, T = self.unpack() S = (1 if sign else 0) self.pack(S, E, T) @@ -367,6 +461,7 @@ def __str__(self): return rv def __abs__(self): + """Compute absolute value""" rv = self.new_mpf() S, E, T = rv.unpack() if not rv.isNaN(): @@ -375,6 +470,7 @@ def __abs__(self): return rv def __neg__(self): + """Compute inverse""" rv = self.new_mpf() S, E, T = rv.unpack() if not rv.isNaN(): @@ -383,6 +479,7 @@ def __neg__(self): return rv def __le__(self, other): + """Test floating-point less than or equal""" assert self.compatible(other) if self.isNaN() or other.isNaN(): return False @@ -390,6 +487,7 @@ def __le__(self, other): return self.partial_order() <= other.partial_order() def __lt__(self, other): + """Test floating-point less than""" assert self.compatible(other) if self.isNaN() or other.isNaN(): return False @@ -397,6 +495,7 @@ def __lt__(self, other): return self.partial_order() < other.partial_order() def __ge__(self, other): + """Test floating-point greater than or equal""" assert self.compatible(other) if self.isNaN() or other.isNaN(): return False @@ -404,6 +503,7 @@ def __ge__(self, other): return self.partial_order() >= other.partial_order() def __gt__(self, other): + """Test floating-point greater than""" assert self.compatible(other) if self.isNaN() or other.isNaN(): return False @@ -411,6 +511,14 @@ def __gt__(self, other): return self.partial_order() > other.partial_order() def __eq__(self, other): + """Test floating-point equality + + Note that this is floating-point equality, so comparisons to + NaN always fail and -0 and +0 are considered equal. + + If you want true equality, use :func:`smtlib_eq` + + """ assert self.compatible(other) if self.isNaN() or other.isNaN(): return False @@ -421,37 +529,63 @@ def __eq__(self, other): # Queries def isZero(self): + """Test if value is zero""" S, E, T = self.unpack() return E == 0 and T == 0 def isSubnormal(self): + """Test if value is subnormal""" S, E, T = self.unpack() return E == 0 and T != 0 def isNormal(self): + """Test if value is normal""" S, E, T = self.unpack() return 1 <= E <= 2 ** self.w - 2 def isNaN(self): + """Test if value is not a number""" S, E, T = self.unpack() return E == 2 ** self.w - 1 and T != 0 def isInfinite(self): + """Test if value is infinite""" S, E, T = self.unpack() return E == 2 ** self.w - 1 and T == 0 def isPositive(self): + """Test if value is positive + + Returns always false for NaN. + """ S, E, T = self.unpack() return not self.isNaN() and S == 0 def isNegative(self): + """Test if value is negative + + Returns always false for NaN. + """ S, E, T = self.unpack() return not self.isNaN() and S == 1 def isFinite(self): + """Test if value is finite + + Returns true for zero, subnormals, or normal numbers. + + Returns false for infinities, and not a number. + """ return self.isZero() or self.isSubnormal() or self.isNormal() def isIntegral(self): + """Test if value is integral + + Returns true if the floating-point value is an integer, and + false in all other cases (including infinities and not a + number). + """ + if self.isFinite(): return self.to_rational().isIntegral() else: @@ -461,6 +595,14 @@ def isIntegral(self): # SMTLIB support def smtlib_sort(self): + """SMT-LIB sort + + Returns "Float16", "Float32", "Float64", or "Float128" if + possible. + + Returns "(_ FloatingPoint *eb* *sb*)" otherwise. + + """ if self.w == 5 and self.p == 11: return "Float16" elif self.w == 8 and self.p == 24: @@ -473,30 +615,46 @@ def smtlib_sort(self): return "(_ FloatingPoint %u %u)" % (self.w, self.p) def smtlib_from_binary_interchange(self): + """SMT-LIB function for converting from bitvector""" return "(_ to_fp %u %u)" % (self.w, self.p) def smtlib_from_float(self): + """SMT-LIB function for converting from FloatingPoint""" return "(_ to_fp %u %u)" % (self.w, self.p) def smtlib_from_real(self): + """SMT-LIB function for converting from Real""" return "(_ to_fp %u %u)" % (self.w, self.p) def smtlib_from_int(self): + """SMT-LIB function for converting from Int""" return "(_ to_fp %u %u)" % (self.w, self.p) def smtlib_from_ubv(self): + """SMT-LIB function for converting from unsigned bitvector""" return "(_ to_fp_unsigned %u %u)" % (self.w, self.p) def smtlib_from_sbv(self): + """SMT-LIB function for converting from signed bitvector""" return "(_ to_fp %u %u)" % (self.w, self.p) def smtlib_to_real(self): + """SMT-LIB function for converting to Real""" return "fp.to_real" def smtlib_to_int(self): + """SMT-LIB function for converting to Int""" return "fp.to_int" def smtlib_literals(self): + """Return list of all possible SMTLIB literals + + Includes: + + * Binary interchange conversion, e.g. ((_ to_fp 8 24) #x00000000) + * FP literal, e.g. (fp #b0 #b00 #b000) + * Special value, e.g. (_ +zero 2 4) + """ choices = [] # Binary interchange @@ -539,9 +697,19 @@ def smtlib_literals(self): return choices def smtlib_literal(self): + """Return an SMT-LIB literal + + Favours special cases, such as (_ +zero 8 24), otherwise + returns literals of the form (fp ...). + + """ return self.smtlib_literals()[-1] def smtlib_random_literal(self): + """Return an SMT-LIB literal (randomly chosen) + + Chooses randomly from :func:`smtlib_literals`. + """ return random.choice(self.smtlib_literals()) def q_round(rm, n): @@ -560,17 +728,13 @@ def fp_add(rm, left, right): cases apply: * NaN propagates - * Adding opposite infinities results in NaN, otherwise infinities propagate + * If the precise result is exactly 0 (i.e. 0 + 0 or a + -a) then we + special case according to Section 6.3 in IEEE-754: - * If the precise result is exactly 0 then we special case - according to Section 6.3 in IEEE-754: - - * we preserve the sign if left and right have the same sign - - * otherwise, we return -0 if the rounding mode is RTN - - * otherwise, we return +0 + * we preserve the sign if left and right have the same sign + * otherwise, we return -0 if the rounding mode is RTN + * otherwise, we return +0 """ assert rm in MPF.ROUNDING_MODES @@ -606,6 +770,14 @@ def fp_add(rm, left, right): return rv def fp_sub(rm, left, right): + """Floating-point substraction + + Performs a correctly rounded :math:`left - right`, which is + equivalent to :math:`left + -right`. + + See :func:`fp_add` for special cases. + + """ assert rm in MPF.ROUNDING_MODES assert left.compatible(right) rv = left.new_mpf() # rv == left @@ -639,6 +811,19 @@ def fp_sub(rm, left, right): return rv def fp_mul(rm, left, right): + """Floating-point multiplication + + Performs a correctly rounded :math:`left * right`. The following + special cases apply: + + * NaN propagates + * Infinities propagate (for non-zero operands) + * Multiplying by zero gives the following results + + * NaN if the other operand is infinite + * 0 of the same sign as the 0 operand + + """ assert rm in MPF.ROUNDING_MODES assert left.compatible(right) sign = (1 if left.isNegative() ^ right.isNegative() else 0) @@ -660,6 +845,23 @@ def fp_mul(rm, left, right): return rv def fp_div(rm, left, right): + """Floating-point division + + Performs a correctly rounded :math:`left \div right`. The + following special cases apply: + + * NaN propagates + * When dividing by infinity + + * NaN when dividing two infinities + * 0 otherwise + + * When dividing by zero + + * NaN when dividing zero by zero + * Infinity otherwise + + """ assert rm in MPF.ROUNDING_MODES assert left.compatible(right) sign = (1 if left.isNegative() ^ right.isNegative() else 0) @@ -685,7 +887,17 @@ def fp_div(rm, left, right): return rv def fp_fma(rm, x, y, z): - # computes (x * y) + z + """Floating-point fused multiply add + + Performs a correctly rounded :math:`x * y + z`. The special cases + of :func:`fp_mul` and :func:`fp_add` apply, and in addition: + + * On a precise 0 result the sign of zero is: + + * Preserved if the sign of x * y is the same as z + * -0 for RTN + * +0 otherwise + """ assert rm in MPF.ROUNDING_MODES assert x.compatible(y) assert x.compatible(z) @@ -725,6 +937,7 @@ def fp_fma(rm, x, y, z): return rv def fp_sqrt(rm, op): + """Floating-point square root""" assert rm in MPF.ROUNDING_MODES # We can get away with approximating the square root to sufficient # precision because of Theorem 19 (p168) of the Handbook of @@ -779,6 +992,7 @@ def fp_sqrt(rm, op): return root def fp_rem(left, right): + """Floating-point remainder""" assert left.compatible(right) rv = left.new_mpf() @@ -798,6 +1012,7 @@ def fp_rem(left, right): return rv def fp_roundToIntegral(rm, op): + """Floating-point round to integer""" assert rm in MPF.ROUNDING_MODES rv = op.new_mpf() @@ -814,6 +1029,7 @@ def fp_roundToIntegral(rm, op): return rv def fp_min(left, right): + """Floating-point minimum""" assert left.compatible(right) if (left.isZero() and right.isZero() and left.isPositive() != right.isPositive()): @@ -827,6 +1043,7 @@ def fp_min(left, right): return left.new_mpf() def fp_max(left, right): + """Floating-point maximum""" assert left.compatible(right) if (left.isZero() and right.isZero() and left.isPositive() != right.isPositive()): @@ -840,10 +1057,12 @@ def fp_max(left, right): return left.new_mpf() def smtlib_eq(left, right): + """Bit-wise equality""" assert left.compatible(right) return (left.isNaN() and right.isNaN()) or (left.bv == right.bv) def fp_nextUp(op): + """Floating-point successor""" rv = op.new_mpf() if op.isNaN(): @@ -878,15 +1097,18 @@ def fp_nextUp(op): return rv def fp_nextDown(op): + """Floating-point predecessor""" # This is how it is defined in 5.3.1 return -fp_nextUp(-op) def fp_from_ubv(eb, sb, rm, op): + """Conversion from unsigned bitvector to MPF""" rv = MPF(eb, sb) rv.from_rational(rm, op.to_unsigned_int()) return rv def fp_to_ubv(op, rm, width): + """Conversion from MPF to unsigned bitvector""" if op.isInfinite() or op.isNaN(): raise Unspecified @@ -900,11 +1122,13 @@ def fp_to_ubv(op, rm, width): raise Unspecified def fp_from_sbv(eb, sb, rm, op): + """Conversion from signed bitvector to MPF""" rv = MPF(eb, sb) rv.from_rational(rm, op.to_signed_int()) return rv def fp_to_sbv(op, rm, width): + """Conversion from MPF to signed bitvector""" if op.isInfinite() or op.isNaN(): raise Unspecified @@ -919,12 +1143,14 @@ def fp_to_sbv(op, rm, width): # ((_ to_fp eb sb) rm op) def fp_from_int(eb, sb, rm, op): + """Conversion from Python integer to MPF""" rv = MPF(eb, sb) rv.from_rational(rm, Rational(op)) return rv # (fp.to_int rm op) def fp_to_int(rm, op): + """Conversion from MPF to Python integer""" if op.isInfinite() or op.isNaN(): raise Unspecified @@ -938,6 +1164,7 @@ def fp_to_int(rm, op): # is where you land when you read 5.4.2), but in 6.3 it says it # doesn't change. def fp_from_float(eb, sb, rm, op): + """Conversion from MPF to MPF (of a different precision)""" rv = MPF(eb, sb) if op.isNaN(): rv.set_nan() diff --git a/mpf/rationals.py b/mpf/rationals.py index b3c084e..1e4dd0a 100644 --- a/mpf/rationals.py +++ b/mpf/rationals.py @@ -23,12 +23,21 @@ ## ## ############################################################################## -# Todo: Make this a subclass of Fraction instead of re-inventing the -# wheel. +""" +This module defines class to deal with Rational numbers. + +.. todo:: This should be a subclass of fractions.Fraction. +""" import fractions class Rational(object): + """Rational number + + *a* is the numerator + + *b* is the denominator + """ def __init__(self, a=0, b=1): assert type(a) is int assert type(b) is int @@ -45,22 +54,31 @@ def __init__(self, a=0, b=1): assert type(self.b) is int def __mul__(self, other): + """Multiplication""" return Rational(self.a*other.a, self.b*other.b) def __truediv__(self, other): + """Division""" return Rational(self.a*other.b, self.b*other.a) def __add__(self, other): + """Addition""" return Rational(self.a*other.b + other.a*self.b, self.b*other.b) def __sub__(self, other): + """Substraction""" return Rational(self.a*other.b - other.a*self.b, self.b*other.b) def __pow__(self, other): + """Exponentiation + + The right-hand side must be an integral Rational, otherwise + AssertionError is raised. + """ assert other.isIntegral() a = fractions.Fraction(self.a, self.b) @@ -69,9 +87,11 @@ def __pow__(self, other): return Rational(p.numerator, p.denominator) def __abs__(self): + """Absolute value""" return Rational(abs(self.a), self.b) def __neg__(self): + """Negation""" return Rational(-self.a, self.b) def __repr__(self): @@ -81,40 +101,60 @@ def __repr__(self): return "Rational(%i, %u)" % (self.a, self.b) def __lt__(self, other): + """<""" return self.a*other.b < other.a*self.b def __le__(self, other): + """<=""" return self.a*other.b <= other.a*self.b def __eq__(self, other): + """Equality""" return self.a*other.b == other.a*self.b def __ne__(self, other): + """Inequality""" return self.a*other.b != other.a*self.b def __gt__(self, other): + """>""" return self.a*other.b > other.a*self.b def __ge__(self, other): + """>=""" return self.a*other.b >= other.a*self.b def isZero(self): + """Test if zero""" return self.a == 0 def isNegative(self): + """Test if negative + + Returns false for 0. + """ return self.a < 0 def isIntegral(self): + """Test if integral""" return (self.a % self.b) == 0 def to_python_int(self): + """Convert to python int""" assert self.isIntegral() return self.a def to_python_float(self): + """Convert to python float""" return float(fractions.Fraction(self.a, self.b)) def to_smtlib(self): + """Convert to SMT-LIB Real expression + + Returns literal for integrals, e.g. "1.0". + + Returns an s-expression otherwise, e.g. "(- (/ 1.0 3.0))". + """ tmp = "%u.0" % abs(self.a) if self.b != 1: tmp = "(/ %s %u.0)" % (tmp, self.b) @@ -123,6 +163,12 @@ def to_smtlib(self): return tmp def to_decimal_string(self): + """Convert to decimal string + + If the fraction does not terminate (e.g. for 1 / 3), an + exception is thrown. + + """ if self.b > 2: b = self.b while b > 1: @@ -154,12 +200,24 @@ def to_decimal_string(self): return rv def q_pow2(n): + """Create rational for 2^n + + *n* can be negative + """ if n >= 0: return Rational(2**n) else: return Rational(1, 2**(-n)) def q_round_to_nearest(n, tiebreak): + """Round to nearest integer + + Round *n* to the nearest integer + + Calls the *tiebreak*(upper, lower) function with the two + alternatives if *n* is precisely between two integers. + + """ lower = (n.a - (n.a % n.b)) / n.b upper = lower + 1 assert Rational(lower) <= n <= Rational(upper) @@ -172,6 +230,7 @@ def q_round_to_nearest(n, tiebreak): return Rational(tiebreak(lower, upper)) def q_round_rne(n): + """Round to nearest even integer""" def tiebreak(lower, upper): if lower % 2 == 0: return lower @@ -181,6 +240,7 @@ def tiebreak(lower, upper): return q_round_to_nearest(n, tiebreak) def q_round_rna(n): + """Round to nearest integer, away from zero""" def tiebreak(lower, upper): if abs(lower) > abs(upper): return lower @@ -189,6 +249,7 @@ def tiebreak(lower, upper): return q_round_to_nearest(n, tiebreak) def q_round_rtz(n): + """Round to nearest integer, towards zero""" i = Rational(abs(n.a) / n.b) if n.isNegative(): return -i @@ -196,6 +257,7 @@ def q_round_rtz(n): return i def q_round_rtp(n): + """Round to nearest integer, towards positive""" if n.isIntegral(): return n i = abs(n.a) / n.b @@ -205,6 +267,7 @@ def q_round_rtp(n): return Rational(i + 1) def q_round_rtn(n): + """Round to nearest integer, towards negative""" if n.isIntegral(): return n i = abs(n.a) / n.b @@ -214,8 +277,7 @@ def q_round_rtn(n): return Rational(i) def q_from_decimal_fragments(sign, integer_part, fraction_part, exp_part): - """ - Build a rational from fragments of a decimal number. + """Build a rational from string fragments of a decimal number. E.g. for "1.23E-1" we have fragments for sign = ""

- F + _ + | C + | F + | I | M + | N + | P + | Q + | R + | S + | T + | U