pypy / pypy / module / math / test /

Full commit
Armin Rigo bdd282c 
Armin Rigo 2a9d4a2 

Amaury Forgeot d… ad5cfb1 
Amaury Forgeot d… 663521b 
Armin Rigo 2a9d4a2 

Armin Rigo 07c5f93 
Alex Gaynor 082cac8 

Armin Rigo 2a9d4a2 

Armin Rigo 07c5f93 

Armin Rigo 2a9d4a2 

Preston Timmons afba3cd 
Armin Rigo 2a9d4a2 

Armin Rigo 07c5f93 

Armin Rigo 2a9d4a2 

Armin Rigo 07c5f93 

Benjamin Peterso… 7e5ac24 
Benjamin Peterso… 94ab1b6 

Benjamin Peterso… 7e5ac24 

Armin Rigo 25d38cc 

Benjamin Peterso… 7e5ac24 

Benjamin Peterso… 0901b1f 

Amaury Forgeot d… 53ce3af 
Benjamin Peterso… 0901b1f 

Amaury Forgeot d… 53ce3af 

Benjamin Peterso… d859a57 
Benjamin Peterso… 752ee85 

Preston Timmons a2bb9ec 

Benjamin Peterso… 752ee85 

Benjamin Peterso… d859a57 

David Schneider 17ffe01 
Benjamin Peterso… d859a57 
Benjamin Peterso… ce1ae14 
Benjamin Peterso… d859a57 

David Schneider 17ffe01 
Benjamin Peterso… d859a57 

Armin Rigo f7b055a 

Armin Rigo bdd282c 

mattip d31590b 
Armin Rigo bdd282c 

Philip Jenvey 4bd535d 

from __future__ import with_statement
import sys
from pypy.module.math.test import test_direct

class AppTestMath:
    spaceconfig = dict(usemodules=['math', 'struct'])

    def setup_class(cls):
        cls.w_cases =
        cls.w_consistent_host =

    def w_ftest(self, actual, expected):
        assert abs(actual - expected) < 10E-5

    def test_all_cases(self):
        if not self.consistent_host:
            skip("please test this on top of PyPy or CPython >= 2.6")
        import math
        for fnname, args, expected in self.cases:
            fn = getattr(math, fnname)
            print(fn, args)
                got = fn(*args)
            except ValueError:
                assert expected == ValueError
            except OverflowError:
                assert expected == OverflowError
                if type(expected) is type(Exception):
                    ok = False
                elif callable(expected):
                    ok = expected(got)
                    gotsign = expectedsign = 1
                    if got < 0.0: gotsign = -gotsign
                    if expected < 0.0: expectedsign = -expectedsign
                    ok = got == expected and gotsign == expectedsign
                if not ok:
                    raise AssertionError("%s(%s): got %s" % (
                        fnname, ', '.join(map(str, args)), got))

    def test_ldexp(self):
        import math
        assert math.ldexp(float("inf"), -10**20) == float("inf")

    def test_fsum(self):
        import math

        # detect evidence of double-rounding: fsum is not always correctly
        # rounded on machines that suffer from double rounding.
        # It is a known problem with IA32 floating-point arithmetic.
        # It should work fine e.g. with x86-64.
        x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer
        HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4)
            skip("fsum is not exact on machines with double rounding")

        test_values = [
            ([], 0.0),
            ([0.0], 0.0),
            ([1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50], 1e-100),
            ([2.0**53, -0.5, -2.0**-54], 2.0**53-1.0),
            ([2.0**53, 1.0, 2.0**-100], 2.0**53+2.0),
            ([2.0**53+10.0, 1.0, 2.0**-100], 2.0**53+12.0),
            ([2.0**53-4.0, 0.5, 2.0**-54], 2.0**53-3.0),
            ([1./n for n in range(1, 1001)],
            ([(-1.)**n/n for n in range(1, 1001)],
            ([1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000], -1.0),
            ([1e16, 1., 1e-16], 10000000000000002.0),
            ([1e16-2., 1.-2.**-53, -(1e16-2.), -(1.-2.**-53)], 0.0),
            # exercise code for resizing partials array
            ([2.**n - 2.**(n+50) + 2.**(n+52) for n in range(-1074, 972, 2)] +

        for i, (vals, expected) in enumerate(test_values):
                actual = math.fsum(vals)
            except OverflowError:
      "test %d failed: got OverflowError, expected %r "
                          "for math.fsum(%.100r)" % (i, expected, vals))
            except ValueError:
      "test %d failed: got ValueError, expected %r "
                          "for math.fsum(%.100r)" % (i, expected, vals))
            assert actual == expected

    def test_factorial(self):
        import math, sys
        assert math.factorial(0) == 1
        assert math.factorial(1) == 1
        assert math.factorial(2) == 2
        assert math.factorial(5) == 120
        assert math.factorial(5.) == 120
        raises(ValueError, math.factorial, -1)
        raises(ValueError, math.factorial, -1.)
        raises(ValueError, math.factorial, 1.1)
        raises(OverflowError, math.factorial, sys.maxsize+1)
        raises(OverflowError, math.factorial, 10e100)

    def test_log1p(self):
        import math
        self.ftest(math.log1p(1/math.e-1), -1)
        self.ftest(math.log1p(0), 0)
        self.ftest(math.log1p(math.e-1), 1)
        self.ftest(math.log1p(1), math.log(2))
        raises(ValueError, math.log1p, -1)
        raises(ValueError, math.log1p, -100)

    def test_acosh(self):
        import math
        self.ftest(math.acosh(1), 0)
        self.ftest(math.acosh(2), 1.3169578969248168)
        assert math.isinf(math.asinh(float("inf")))
        raises(ValueError, math.acosh, 0)

    def test_asinh(self):
        import math
        self.ftest(math.asinh(0), 0)
        self.ftest(math.asinh(1), 0.88137358701954305)
        self.ftest(math.asinh(-1), -0.88137358701954305)
        assert math.isinf(math.asinh(float("inf")))

    def test_atanh(self):
        import math
        self.ftest(math.atanh(0), 0)
        self.ftest(math.atanh(0.5), 0.54930614433405489)
        self.ftest(math.atanh(-0.5), -0.54930614433405489)
        raises(ValueError, math.atanh, 1.)
        assert math.isnan(math.atanh(float("nan")))

    def test_mtestfile(self):
        import math
        import zipfile
        import os
        import struct
        def _parse_mtestfile(fname):
            """Parse a file with test values

            -- starts a comment
            blank lines, or lines containing only a comment, are ignored
            other lines are expected to have the form
              id fn arg -> expected [flag]*

            with open(fname) as fp:
                for line in fp:
                    # strip comments, and skip blank lines
                    if '--' in line:
                        line = line[:line.index('--')]
                    if not line.strip():

                    lhs, rhs = line.split('->')
                    id, fn, arg = lhs.split()
                    rhs_pieces = rhs.split()
                    exp = rhs_pieces[0]
                    flags = rhs_pieces[1:]

                    yield (id, fn, float(arg), float(exp), flags)
        def to_ulps(x):
            """Convert a non-NaN float x to an integer, in such a way that
            adjacent floats are converted to adjacent integers.  Then
            abs(ulps(x) - ulps(y)) gives the difference in ulps between two

            The results from this function will only make sense on platforms
            where C doubles are represented in IEEE 754 binary64 format.

            n = struct.unpack('<q', struct.pack('<d', x))[0]
            if n < 0:
                n = ~(n+2**63)
            return n

        def ulps_check(expected, got, ulps=20):
            """Given non-NaN floats `expected` and `got`,
            check that they're equal to within the given number of ulps.

            Returns None on success and an error message on failure."""

            ulps_error = to_ulps(got) - to_ulps(expected)
            if abs(ulps_error) <= ulps:
                return None
            return "error = {} ulps; permitted error = {} ulps".format(ulps_error,

        def acc_check(expected, got, rel_err=2e-15, abs_err = 5e-323):
            """Determine whether non-NaN floats a and b are equal to within a
            (small) rounding error.  The default values for rel_err and
            abs_err are chosen to be suitable for platforms where a float is
            represented by an IEEE 754 double.  They allow an error of between
            9 and 19 ulps."""

            # need to special case infinities, since inf - inf gives nan
            if math.isinf(expected) and got == expected:
                return None

            error = got - expected

            permitted_error = max(abs_err, rel_err * abs(expected))
            if abs(error) < permitted_error:
                return None
            return "error = {}; permitted error = {}".format(error,

        ALLOWED_ERROR = 20  # permitted error, in ulps
        fail_fmt = "{}:{}({!r}): expected {!r}, got {!r}"

        failures = []
        math_testcases = os.path.join(os.path.dirname(zipfile.__file__), "test",
        for id, fn, arg, expected, flags in _parse_mtestfile(math_testcases):
            func = getattr(math, fn)

            if 'invalid' in flags or 'divide-by-zero' in flags:
                expected = 'ValueError'
            elif 'overflow' in flags:
                expected = 'OverflowError'

                got = func(arg)
            except ValueError:
                got = 'ValueError'
            except OverflowError:
                got = 'OverflowError'

            accuracy_failure = None
            if isinstance(got, float) and isinstance(expected, float):
                if math.isnan(expected) and math.isnan(got):
                if not math.isnan(expected) and not math.isnan(got):
                    if fn == 'lgamma':
                        # we use a weaker accuracy test for lgamma;
                        # lgamma only achieves an absolute error of
                        # a few multiples of the machine accuracy, in
                        # general.
                        accuracy_failure = acc_check(expected, got,
                                                  rel_err = 5e-15,
                                                  abs_err = 5e-15)
                    elif fn == 'erfc':
                        # erfc has less-than-ideal accuracy for large
                        # arguments (x ~ 25 or so), mainly due to the
                        # error involved in computing exp(-x*x).
                        # XXX Would be better to weaken this test only
                        # for large x, instead of for all x.
                        accuracy_failure = ulps_check(expected, got, 2000)

                        accuracy_failure = ulps_check(expected, got, 20)
                    if accuracy_failure is None:

            if isinstance(got, str) and isinstance(expected, str):
                if got == expected:

            fail_msg = fail_fmt.format(id, fn, arg, expected, got)
            if accuracy_failure is not None:
                fail_msg += ' ({})'.format(accuracy_failure)
        assert not failures

    def test_trunc(self):
        import math
        assert math.trunc(1.9) == 1.0
        raises((AttributeError, TypeError), math.trunc, 1.9j)
        class foo(object):
            def __trunc__(self):
                return "truncated"
        assert math.trunc(foo()) == "truncated"

    def test_copysign_nan(self):
        skip('sign of nan is undefined')
        import math
        assert math.copysign(1.0, float('-nan')) == -1.0

    def test_special_methods(self):
        import math
        class Z:
        for i, name in enumerate(('ceil', 'floor', 'trunc')):
            setattr(Z, '__{}__'.format(name), lambda self: i)
            func = getattr(math, name)
            assert func(Z()) == i