File size: 5,227 Bytes
b200bda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from mpmath import *
from mpmath.libmp import *

import random

def test_fractional_pow():
    mp.dps = 15
    assert mpf(16) ** 2.5 == 1024
    assert mpf(64) ** 0.5 == 8
    assert mpf(64) ** -0.5 == 0.125
    assert mpf(16) ** -2.5 == 0.0009765625
    assert (mpf(10) ** 0.5).ae(3.1622776601683791)
    assert (mpf(10) ** 2.5).ae(316.2277660168379)
    assert (mpf(10) ** -0.5).ae(0.31622776601683794)
    assert (mpf(10) ** -2.5).ae(0.0031622776601683794)
    assert (mpf(10) ** 0.3).ae(1.9952623149688795)
    assert (mpf(10) ** -0.3).ae(0.50118723362727224)

def test_pow_integer_direction():
    """
    Test that inexact integer powers are rounded in the right
    direction.
    """
    random.seed(1234)
    for prec in [10, 53, 200]:
        for i in range(50):
            a = random.randint(1<<(prec-1), 1<<prec)
            b = random.randint(2, 100)
            ab = a**b
            # note: could actually be exact, but that's very unlikely!
            assert to_int(mpf_pow(from_int(a), from_int(b), prec, round_down)) < ab
            assert to_int(mpf_pow(from_int(a), from_int(b), prec, round_up)) > ab


def test_pow_epsilon_rounding():
    """
    Stress test directed rounding for powers with integer exponents.
    Basically, we look at the following cases:

    >>> 1.0001 ** -5 # doctest: +SKIP
    0.99950014996500702
    >>> 0.9999 ** -5 # doctest: +SKIP
    1.000500150035007
    >>> (-1.0001) ** -5 # doctest: +SKIP
    -0.99950014996500702
    >>> (-0.9999) ** -5 # doctest: +SKIP
    -1.000500150035007

    >>> 1.0001 ** -6 # doctest: +SKIP
    0.99940020994401269
    >>> 0.9999 ** -6 # doctest: +SKIP
    1.0006002100560125
    >>> (-1.0001) ** -6 # doctest: +SKIP
    0.99940020994401269
    >>> (-0.9999) ** -6 # doctest: +SKIP
    1.0006002100560125

    etc.

    We run the tests with values a very small epsilon away from 1:
    small enough that the result is indistinguishable from 1 when
    rounded to nearest at the output precision. We check that the
    result is not erroneously rounded to 1 in cases where the
    rounding should be done strictly away from 1.
    """

    def powr(x, n, r):
        return make_mpf(mpf_pow_int(x._mpf_, n, mp.prec, r))

    for (inprec, outprec) in [(100, 20), (5000, 3000)]:

        mp.prec = inprec

        pos10001 = mpf(1) + mpf(2)**(-inprec+5)
        pos09999 = mpf(1) - mpf(2)**(-inprec+5)
        neg10001 = -pos10001
        neg09999 = -pos09999

        mp.prec = outprec
        r = round_up
        assert powr(pos10001, 5, r) > 1
        assert powr(pos09999, 5, r) == 1
        assert powr(neg10001, 5, r) < -1
        assert powr(neg09999, 5, r) == -1
        assert powr(pos10001, 6, r) > 1
        assert powr(pos09999, 6, r) == 1
        assert powr(neg10001, 6, r) > 1
        assert powr(neg09999, 6, r) == 1

        assert powr(pos10001, -5, r) == 1
        assert powr(pos09999, -5, r) > 1
        assert powr(neg10001, -5, r) == -1
        assert powr(neg09999, -5, r) < -1
        assert powr(pos10001, -6, r) == 1
        assert powr(pos09999, -6, r) > 1
        assert powr(neg10001, -6, r) == 1
        assert powr(neg09999, -6, r) > 1

        r = round_down
        assert powr(pos10001, 5, r) == 1
        assert powr(pos09999, 5, r) < 1
        assert powr(neg10001, 5, r) == -1
        assert powr(neg09999, 5, r) > -1
        assert powr(pos10001, 6, r) == 1
        assert powr(pos09999, 6, r) < 1
        assert powr(neg10001, 6, r) == 1
        assert powr(neg09999, 6, r) < 1

        assert powr(pos10001, -5, r) < 1
        assert powr(pos09999, -5, r) == 1
        assert powr(neg10001, -5, r) > -1
        assert powr(neg09999, -5, r) == -1
        assert powr(pos10001, -6, r) < 1
        assert powr(pos09999, -6, r) == 1
        assert powr(neg10001, -6, r) < 1
        assert powr(neg09999, -6, r) == 1

        r = round_ceiling
        assert powr(pos10001, 5, r) > 1
        assert powr(pos09999, 5, r) == 1
        assert powr(neg10001, 5, r) == -1
        assert powr(neg09999, 5, r) > -1
        assert powr(pos10001, 6, r) > 1
        assert powr(pos09999, 6, r) == 1
        assert powr(neg10001, 6, r) > 1
        assert powr(neg09999, 6, r) == 1

        assert powr(pos10001, -5, r) == 1
        assert powr(pos09999, -5, r) > 1
        assert powr(neg10001, -5, r) > -1
        assert powr(neg09999, -5, r) == -1
        assert powr(pos10001, -6, r) == 1
        assert powr(pos09999, -6, r) > 1
        assert powr(neg10001, -6, r) == 1
        assert powr(neg09999, -6, r) > 1

        r = round_floor
        assert powr(pos10001, 5, r) == 1
        assert powr(pos09999, 5, r) < 1
        assert powr(neg10001, 5, r) < -1
        assert powr(neg09999, 5, r) == -1
        assert powr(pos10001, 6, r) == 1
        assert powr(pos09999, 6, r) < 1
        assert powr(neg10001, 6, r) == 1
        assert powr(neg09999, 6, r) < 1

        assert powr(pos10001, -5, r) < 1
        assert powr(pos09999, -5, r) == 1
        assert powr(neg10001, -5, r) == -1
        assert powr(neg09999, -5, r) < -1
        assert powr(pos10001, -6, r) < 1
        assert powr(pos09999, -6, r) == 1
        assert powr(neg10001, -6, r) < 1
        assert powr(neg09999, -6, r) == 1

    mp.dps = 15