[pypy-commit] pypy default: merge bigint-with-int-ops

cfbolz noreply at buildbot.pypy.org
Fri Oct 17 08:53:48 CEST 2014


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r73985:d5cec19910a1
Date: 2014-10-17 08:53 +0200
http://bitbucket.org/pypy/pypy/changeset/d5cec19910a1/

Log:	merge bigint-with-int-ops

	this is only for the rbigint changes, leaving the branch open to at
	some point be able to do the objspace stuff

diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -44,6 +44,25 @@
 MASK = int((1 << SHIFT) - 1)
 FLOAT_MULTIPLIER = float(1 << SHIFT)
 
+# For BIGINT and INT mix.
+#
+# The VALID range of an int is different than a valid range of a bigint of length one.
+# -1 << LONG_BIT is actually TWO digits, because they are stored without the sign.
+if SHIFT == LONG_BIT - 1:
+    MIN_INT_VALUE = -1 << SHIFT
+    def int_in_valid_range(x):
+        if x == MIN_INT_VALUE:
+            return False
+        return True
+else:
+    # Means we don't have INT128 on 64bit.
+    def int_in_valid_range(x):
+        if x > MASK or x < -MASK:
+            return False
+        return True
+
+int_in_valid_range._always_inline_ = True
+
 # Debugging digit array access.
 #
 # False == no checking at all
@@ -485,10 +504,27 @@
             i += 1
         return True
 
+    @jit.elidable
+    def int_eq(self, other):
+        """ eq with int """
+        
+        if not int_in_valid_range(other):
+            # Fallback to Long. 
+            return self.eq(rbigint.fromint(other))
+
+        if self.numdigits() > 1:
+            return False
+
+        return (self.sign * self.digit(0)) == other
+
     @jit.look_inside
     def ne(self, other):
         return not self.eq(other)
 
+    @jit.look_inside
+    def int_ne(self, other):
+        return not self.int_eq(other)
+
     @jit.elidable
     def lt(self, other):
         if self.sign > other.sign:
@@ -524,18 +560,67 @@
             i -= 1
         return False
 
+    @jit.elidable
+    def int_lt(self, other):
+        """ lt where other is an int """
+
+        if not int_in_valid_range(other):
+            # Fallback to Long.
+            return self.lt(rbigint.fromint(other))
+
+        osign = 1
+        if other == 0:
+            osign = 0
+        elif other < 0:
+            osign = -1
+ 
+        if self.sign > osign:
+            return False
+        elif self.sign < osign:
+            return True
+
+        digits = self.numdigits()
+        
+        if digits > 1:
+            if osign == 1:
+                return False
+            else:
+                return True
+
+        d1 = self.sign * self.digit(0)
+        if d1 < other:
+            return True
+        return False
+
     @jit.look_inside
     def le(self, other):
         return not other.lt(self)
 
     @jit.look_inside
+    def int_le(self, other):
+        # Alternative that might be faster, reimplant this. as a check with other + 1. But we got to check for overflow
+        # or reduce valid range.
+
+        if self.int_eq(other):
+            return True
+        return self.int_lt(other)
+
+    @jit.look_inside
     def gt(self, other):
         return other.lt(self)
 
     @jit.look_inside
+    def int_gt(self, other):
+        return not self.int_le(other)
+
+    @jit.look_inside
     def ge(self, other):
         return not self.lt(other)
 
+    @jit.look_inside
+    def int_ge(self, other):
+        return not self.int_lt(other)
+
     @jit.elidable
     def hash(self):
         return _hash(self)
@@ -554,12 +639,31 @@
         return result
 
     @jit.elidable
+    def int_add(self, other):
+        if not int_in_valid_range(other):
+            # Fallback to long.
+            return self.add(rbigint.fromint(other))
+        elif self.sign == 0:
+            return rbigint.fromint(other)
+        elif other == 0:
+            return self
+
+        sign = -1 if other < 0 else 1
+        if self.sign == sign:
+            result = _x_int_add(self, other)
+        else:
+            result = _x_int_sub(self, other)
+            result.sign *= -1
+        result.sign *= sign
+        return result
+
+    @jit.elidable
     def sub(self, other):
         if other.sign == 0:
             return self
-        if self.sign == 0:
+        elif self.sign == 0:
             return rbigint(other._digits[:other.size], -other.sign, other.size)
-        if self.sign == other.sign:
+        elif self.sign == other.sign:
             result = _x_sub(self, other)
         else:
             result = _x_add(self, other)
@@ -567,6 +671,22 @@
         return result
 
     @jit.elidable
+    def int_sub(self, other):
+        if not int_in_valid_range(other):
+            # Fallback to long.
+            return self.sub(rbigint.fromint(other))
+        elif other == 0:
+            return self
+        elif self.sign == 0:
+            return rbigint.fromint(-other)
+        elif self.sign == (-1 if other < 0 else 1):
+            result = _x_int_sub(self, other)
+        else:
+            result = _x_int_add(self, other)
+        result.sign *= self.sign
+        return result
+
+    @jit.elidable
     def mul(self, b):
         asize = self.numdigits()
         bsize = b.numdigits()
@@ -612,6 +732,37 @@
         return result
 
     @jit.elidable
+    def int_mul(self, b):
+        if not int_in_valid_range(b):
+            # Fallback to long.
+            return self.mul(rbigint.fromint(b))
+
+        if self.sign == 0 or b == 0:
+            return NULLRBIGINT
+
+        asize = self.numdigits()
+        digit = abs(b)
+        bsign = -1 if b < 0 else 1
+
+        if digit == 1:
+            return rbigint(self._digits[:self.size], self.sign * bsign, asize)
+        elif asize == 1:
+            res = self.widedigit(0) * digit
+            carry = res >> SHIFT
+            if carry:
+                return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * bsign, 2)
+            else:
+                return rbigint([_store_digit(res & MASK)], self.sign * bsign, 1)
+
+        elif digit & (digit - 1) == 0:
+            result = self.lqshift(ptwotable[digit])
+        else:
+            result = _muladd1(self, digit)
+
+        result.sign = self.sign * bsign
+        return result
+
+    @jit.elidable
     def truediv(self, other):
         div = _bigint_true_divide(self, other)
         return div
@@ -629,7 +780,7 @@
         if mod.sign * other.sign == -1:
             if div.sign == 0:
                 return ONENEGATIVERBIGINT
-            div = div.sub(ONERBIGINT)
+            div = div.int_sub(1)
 
         return div
 
@@ -652,7 +803,7 @@
                     return ONENEGATIVERBIGINT if other.sign == -1 else ONERBIGINT
                 return NULLRBIGINT
             elif digit & (digit - 1) == 0:
-                mod = self.and_(rbigint([_store_digit(digit - 1)], 1, 1))
+                mod = self.int_and_(digit - 1)
             else:
                 # Perform
                 size = self.numdigits() - 1
@@ -675,6 +826,48 @@
         return mod
 
     @jit.elidable
+    def int_mod(self, other):
+        if self.sign == 0:
+            return NULLRBIGINT
+
+        elif not int_in_valid_range(other):
+            # Fallback to long.
+            return self.mod(rbigint.fromint(other))
+
+        elif other != 0:
+            digit = abs(other)
+            if digit == 1:
+                return NULLRBIGINT
+            elif digit == 2:
+                modm = self.digit(0) & 1
+                if modm:
+                    return ONENEGATIVERBIGINT if other < 0 else ONERBIGINT
+                return NULLRBIGINT
+            elif digit & (digit - 1) == 0:
+                mod = self.int_and_(digit - 1)
+            else:
+                # Perform
+                size = self.numdigits() - 1
+                if size > 0:
+                    rem = self.widedigit(size)
+                    size -= 1
+                    while size >= 0:
+                        rem = ((rem << SHIFT) + self.widedigit(size)) % digit
+                        size -= 1
+                else:
+                    rem = self.digit(0) % digit
+
+                if rem == 0:
+                    return NULLRBIGINT
+                mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1, 1)
+        else:
+            raise ZeroDivisionError("long division or modulo by zero")
+
+        if mod.sign * (-1 if other < 0 else 1) == -1:
+            mod = mod.int_add(other)
+        return mod
+
+    @jit.elidable
     def divmod(v, w):
         """
         The / and % operators are now defined in terms of divmod().
@@ -697,7 +890,7 @@
             mod = mod.add(w)
             if div.sign == 0:
                 return ONENEGATIVERBIGINT, mod
-            div = div.sub(ONERBIGINT)
+            div = div.int_sub(1)
         return div, mod
 
     @jit.elidable
@@ -855,7 +1048,7 @@
         if self.sign == 0:
             return ONENEGATIVERBIGINT
 
-        ret = self.add(ONERBIGINT)
+        ret = self.int_add(1)
         ret.sign = -ret.sign
         return ret
 
@@ -1000,14 +1193,26 @@
         return _bitwise(self, '&', other)
 
     @jit.elidable
+    def int_and_(self, other):
+        return _int_bitwise(self, '&', other)
+
+    @jit.elidable
     def xor(self, other):
         return _bitwise(self, '^', other)
 
     @jit.elidable
+    def int_xor(self, other):
+        return _int_bitwise(self, '^', other)
+
+    @jit.elidable
     def or_(self, other):
         return _bitwise(self, '|', other)
 
     @jit.elidable
+    def int_or_(self, other):
+        return _int_bitwise(self, '|', other)
+
+    @jit.elidable
     def oct(self):
         if self.sign == 0:
             return '0L'
@@ -1190,6 +1395,25 @@
     z._normalize()
     return z
 
+def _x_int_add(a, b):
+    """ Add the absolute values of one bigint and one integer. """
+    size_a = a.numdigits()
+
+    z = rbigint([NULLDIGIT] * (size_a + 1), 1)
+    i = UDIGIT_TYPE(1)
+    carry = a.udigit(0) + abs(b)
+    z.setdigit(0, carry)
+    carry >>= SHIFT
+
+    while i < size_a:
+        carry += a.udigit(i)
+        z.setdigit(i, carry)
+        carry >>= SHIFT
+        i += 1
+    z.setdigit(i, carry)
+    z._normalize()
+    return z
+
 def _x_sub(a, b):
     """ Subtract the absolute values of two integers. """
 
@@ -1236,6 +1460,42 @@
     z._normalize()
     return z
 
+def _x_int_sub(a, b):
+    """ Subtract the absolute values of two integers. """
+
+    size_a = a.numdigits()
+
+    bdigit = abs(b)
+
+    if size_a == 1:
+        # Find highest digit where a and b differ:
+        adigit = a.digit(0)
+
+        if adigit == bdigit:
+            return NULLRBIGINT
+    
+        return rbigint.fromint(adigit - bdigit)
+
+    z = rbigint([NULLDIGIT] * size_a, 1, size_a)
+    i = _load_unsigned_digit(1)
+    # The following assumes unsigned arithmetic
+    # works modulo 2**N for some N>SHIFT.
+    borrow = a.udigit(0) - bdigit
+    z.setdigit(0, borrow)
+    borrow >>= SHIFT
+    #borrow &= 1 # Keep only one sign bit
+
+    while i < size_a:
+        borrow = a.udigit(i) - borrow
+        z.setdigit(i, borrow)
+        borrow >>= SHIFT
+        #borrow &= 1
+        i += 1
+
+    assert borrow == 0
+    z._normalize()
+    return z
+
 # A neat little table of power of twos.
 ptwotable = {}
 for x in range(SHIFT-1):
@@ -2343,6 +2603,89 @@
     return z.invert()
 _bitwise._annspecialcase_ = "specialize:arg(1)"
 
+def _int_bitwise(a, op, b): # '&', '|', '^'
+    """ Bitwise and/or/xor operations """
+
+    if not int_in_valid_range(b):
+        # Fallback to long.
+        return _bitwise(a, op, rbigint.fromint(b))
+
+    if a.sign < 0:
+        a = a.invert()
+        maska = MASK
+    else:
+        maska = 0
+    if b < 0:
+        b = ~b
+        maskb = MASK
+    else:
+        maskb = 0
+
+    negz = 0
+    if op == '^':
+        if maska != maskb:
+            maska ^= MASK
+            negz = -1
+    elif op == '&':
+        if maska and maskb:
+            op = '|'
+            maska ^= MASK
+            maskb ^= MASK
+            negz = -1
+    elif op == '|':
+        if maska or maskb:
+            op = '&'
+            maska ^= MASK
+            maskb ^= MASK
+            negz = -1
+
+    # JRH: The original logic here was to allocate the result value (z)
+    # as the longer of the two operands.  However, there are some cases
+    # where the result is guaranteed to be shorter than that: AND of two
+    # positives, OR of two negatives: use the shorter number.  AND with
+    # mixed signs: use the positive number.  OR with mixed signs: use the
+    # negative number.  After the transformations above, op will be '&'
+    # iff one of these cases applies, and mask will be non-0 for operands
+    # whose length should be ignored.
+
+    size_a = a.numdigits()
+    if op == '&':
+        if maska:
+            size_z = 1
+        else:
+            if maskb:
+                size_z = size_a
+            else:
+                size_z = 1
+    else:
+        size_z = size_a
+
+    z = rbigint([NULLDIGIT] * size_z, 1, size_z)
+    i = 0
+    while i < size_z:
+        if i < size_a:
+            diga = a.digit(i) ^ maska
+        else:
+            diga = maska
+        if i == 0:
+            digb = b ^ maskb
+        else:
+            digb = maskb
+
+        if op == '&':
+            z.setdigit(i, diga & digb)
+        elif op == '|':
+            z.setdigit(i, diga | digb)
+        elif op == '^':
+            z.setdigit(i, diga ^ digb)
+        i += 1
+
+    z._normalize()
+    if negz == 0:
+        return z
+
+    return z.invert()
+_int_bitwise._annspecialcase_ = "specialize:arg(1)"
 
 ULONGLONG_BOUND = r_ulonglong(1L << (r_longlong.BITS-1))
 LONGLONG_MIN = r_longlong(-(1L << (r_longlong.BITS-1)))
diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py
--- a/rpython/rlib/test/test_rbigint.py
+++ b/rpython/rlib/test/test_rbigint.py
@@ -2,7 +2,8 @@
 
 import operator
 import sys
-from random import random, randint, sample
+import math
+from random import random, randint, sample, seed
 
 import py
 
@@ -14,6 +15,14 @@
 from rpython.rtyper.test.test_llinterp import interpret
 from rpython.translator.c.test.test_standalone import StandaloneTests
 
+long_vals_not_too_big = range(17) + [
+        37, 50,
+        127, 128, 129, 511, 512, 513, sys.maxint, sys.maxint + 1,
+        123456789123456789000000L,
+        ]
+
+long_vals = long_vals_not_too_big + [
+        1 << 100, 3 ** 10000]
 
 class TestRLong(object):
     def test_simple(self):
@@ -43,19 +52,23 @@
             assert r2.str() == str(-n)
 
     def test_floordiv(self):
-        for op1 in [-12, -2, -1, 1, 2, 50]:
-            for op2 in [-4, -2, -1, 1, 2, 8]:
-                rl_op1 = rbigint.fromint(op1)
-                rl_op2 = rbigint.fromint(op2)
+        for op1 in gen_signs(long_vals):
+            for op2 in gen_signs(long_vals):
+                if not op2:
+                    continue
+                rl_op1 = rbigint.fromlong(op1)
+                rl_op2 = rbigint.fromlong(op2)
                 r1 = rl_op1.floordiv(rl_op2)
                 r2 = op1 // op2
                 assert r1.tolong() == r2
 
     def test_truediv(self):
-        for op1 in [-12, -2, -1, 1, 2, 50]:
-            for op2 in [-4, -2, -1, 1, 2, 8]:
-                rl_op1 = rbigint.fromint(op1)
-                rl_op2 = rbigint.fromint(op2)
+        for op1 in gen_signs(long_vals_not_too_big):
+            for op2 in gen_signs(long_vals):
+                if not op2:
+                    continue
+                rl_op1 = rbigint.fromlong(op1)
+                rl_op2 = rbigint.fromlong(op2)
                 r1 = rl_op1.truediv(rl_op2)
                 r2 = op1 / op2
                 assert r1 == r2
@@ -98,19 +111,29 @@
         assert f == -1.7976931348623157e+308   # exactly
 
     def test_mod(self):
-        for op1 in [-50, -12, -2, -1, 1, 2, 50, 52]:
-            for op2 in [-4, -2, -1, 1, 2, 8]:
-                rl_op1 = rbigint.fromint(op1)
-                rl_op2 = rbigint.fromint(op2)
+        for op1 in gen_signs(long_vals):
+            for op2 in gen_signs(long_vals):
+                if not op2:
+                    continue
+                rl_op1 = rbigint.fromlong(op1)
+                rl_op2 = rbigint.fromlong(op2)
                 r1 = rl_op1.mod(rl_op2)
                 r2 = op1 % op2
                 print op1, op2
                 assert r1.tolong() == r2
 
+    def test_int_mod(self):
+        for x in gen_signs(long_vals):
+            for y in gen_signs([1, 2, 4, 8, 8888, sys.maxint, 2 ** 19, 2 ** 18 - 1]):
+                op1 = rbigint.fromlong(x)
+                r1 = op1.int_mod(y)
+                r2 = x % y
+                assert r1.tolong() == r2
+
     def test_pow(self):
-        for op1 in [-50, -12, -2, -1, 1, 2, 50, 52]:
+        for op1 in gen_signs(long_vals_not_too_big):
             for op2 in [0, 1, 2, 8, 9, 10, 11]:
-                rl_op1 = rbigint.fromint(op1)
+                rl_op1 = rbigint.fromlong(op1)
                 rl_op2 = rbigint.fromint(op2)
                 r1 = rl_op1.pow(rl_op2)
                 r2 = op1 ** op2
@@ -238,6 +261,13 @@
                 result = f1.add(f2)
                 assert result.tolong() == x * i + y * j
 
+    def test_int_add(self):
+        for x in gen_signs(long_vals):
+            for y in gen_signs([0, 1, 9999, sys.maxint, 2 ** 19, 2 ** 18 - 1]):
+                f1 = rbigint.fromlong(x)
+                result = f1.int_add(y)
+                assert result.tolong() == x + y
+
     def test_sub(self):
         x = 12378959520302182384345L
         y = 88961284756491823819191823L
@@ -248,20 +278,34 @@
                 result = f1.sub(f2)
                 assert result.tolong() == x * i - y * j
 
+    def test_int_sub(self):
+        for x in gen_signs([0, 123456789123456789000000L, 1 << 100, 3 ** 10000]):
+            for y in gen_signs([0, 1, 8888, sys.maxint, 2 ** 19, 2 ** 18 - 1]):
+                f1 = rbigint.fromlong(x)
+                result = f1.int_sub(y)
+                assert result.tolong() == x - y
+
     def test_subzz(self):
         w_l0 = rbigint.fromint(0)
         assert w_l0.sub(w_l0).tolong() == 0
 
     def test_mul(self):
-        x = -1238585838347L
-        y = 585839391919233L
-        f1 = rbigint.fromlong(x)
-        f2 = rbigint.fromlong(y)
-        result = f1.mul(f2)
-        assert result.tolong() == x * y
-        # also test a * a, it has special code
-        result = f1.mul(f1)
-        assert result.tolong() == x * x
+        for x in gen_signs(long_vals):
+            f1 = rbigint.fromlong(x)
+            for y in gen_signs(long_vals_not_too_big):
+                f2 = rbigint.fromlong(y)
+                result = f1.mul(f2)
+                assert result.tolong() == x * y
+            # there's a special case for a is b
+            result = f1.mul(f1)
+            assert result.tolong() == x * x
+
+    def test_int_mul(self):
+        for x in gen_signs([39, 128, 111111111, 123456789123456789000000L, 1 << 100, 3 ** 10000]):
+            for y in gen_signs([0, 1, 8888, sys.maxint, 2 ** 19, 2 ** 18 - 1]):
+                f1 = rbigint.fromlong(x)
+                result = f1.int_mul(y)
+                assert result.tolong() == x * y
 
     def test_tofloat(self):
         x = 12345678901234567890L ** 10
@@ -309,6 +353,9 @@
         f1 = rbigint.fromfloat(9007199254740991.0)
         assert f1.tolong() == 9007199254740991
 
+        null = rbigint.fromfloat(-0.0)
+        assert null.int_eq(0)
+
     def test_eq(self):
         x = 5858393919192332223L
         y = 585839391919233111223311112332L
@@ -336,6 +383,17 @@
                 f2 = rbigint.fromlong(y)
                 assert (x < y) ==  f1.lt(f2)
 
+    def test_int_comparison(self):
+        for x in gen_signs(long_vals):
+            for y in gen_signs([0, 1, 0x11111111, 0x11111112, 8888, sys.maxint, 2 ** 19, 2 ** 18 - 1]):
+                f1 = rbigint.fromlong(x)
+                assert (x < y) ==  f1.int_lt(y)
+                assert (x <= y) ==  f1.int_le(y)
+                assert (x > y) ==  f1.int_gt(y)
+                assert (x >= y) ==  f1.int_ge(y)
+                assert (x == y) ==  f1.int_eq(y)
+                assert (x != y) ==  f1.int_ne(y)
+
     def test_order(self):
         f6 = rbigint.fromint(6)
         f7 = rbigint.fromint(7)
@@ -344,6 +402,14 @@
         assert (f6.gt(f6), f6.gt(f7), f7.gt(f6)) == (0,0,1)
         assert (f6.ge(f6), f6.ge(f7), f7.ge(f6)) == (1,0,1)
 
+    def test_int_order(self):
+        f6 = rbigint.fromint(6)
+        f7 = rbigint.fromint(7)
+        assert (f6.int_lt(6), f6.int_lt(7), f7.int_lt(6)) == (0,1,0)
+        assert (f6.int_le(6), f6.int_le(7), f7.int_le(6)) == (1,1,0)
+        assert (f6.int_gt(6), f6.int_gt(7), f7.int_gt(6)) == (0,0,1)
+        assert (f6.int_ge(6), f6.int_ge(7), f7.int_ge(6)) == (1,0,1)
+
     def test_int_conversion(self):
         f1 = rbigint.fromlong(12332)
         f2 = rbigint.fromint(12332)
@@ -389,7 +455,6 @@
 
 
     def test_pow_lll(self):
-        return
         x = 10L
         y = 2L
         z = 13L
@@ -514,12 +579,21 @@
                     res2 = getattr(operator, mod)(x, y)
                     assert res1 == res2
 
+    def test_int_bitwise(self):
+        for x in gen_signs([0, 1, 5, 11, 42, 43, 2 ** 30]):
+            for y in gen_signs([0, 1, 5, 11, 42, 43, 3 ** 30, 2 ** 31]):
+                lx = rbigint.fromlong(x)
+                for mod in "xor and_ or_".split():
+                    res1 = getattr(lx, 'int_' + mod)(y).tolong()
+                    res2 = getattr(operator, mod)(x, y)
+                    assert res1 == res2
+
     def test_mul_eq_shift(self):
         p2 = rbigint.fromlong(1).lshift(63)
         f1 = rbigint.fromlong(0).lshift(63)
         f2 = rbigint.fromlong(0).mul(p2)
         assert f1.eq(f2)
-            
+
     def test_tostring(self):
         z = rbigint.fromlong(0)
         assert z.str() == '0'
@@ -534,6 +608,11 @@
         assert x.format('.!') == (
             '-!....!!..!!..!.!!.!......!...!...!!!........!')
         assert x.format('abcdefghijkl', '<<', '>>') == '-<<cakdkgdijffjf>>'
+        x = rbigint.fromlong(-18471379832321000000000000000000000000000000000000000000)
+        assert x.str() == '-18471379832321000000000000000000000000000000000000000000'
+        assert x.repr() == '-18471379832321000000000000000000000000000000000000000000L'
+        assert x.hex() == '-0xc0d9a6f41fbcf1718b618443d45516a051e40000000000L'
+        assert x.oct() == '-014033151572037571705614266060420752125055201217100000000000000L'
 
     def test_format_caching(self):
         big = rbigint.fromlong(2 ** 1000)
@@ -579,6 +658,18 @@
             assert rbigint.fromlong(x).hash() == rbigint.fromlong(y).hash()
             assert rbigint.fromlong(-x).hash() == rbigint.fromlong(-y).hash()
 
+    def test_log(self):
+        from rpython.rlib.rfloat import ulps_check
+        for op in long_vals:
+            if not op:
+                continue
+            for base in [0, 2, 4, 8, 16, 10, math.e]:
+                l = rbigint.fromlong(op).log(base)
+                if base:
+                    assert ulps_check(l, math.log(op, base), 1) is None
+                else:
+                    assert ulps_check(l, math.log(op), 1) is None
+
 class TestInternalFunctions(object):
     def test__inplace_divrem1(self):
         # signs are not handled in the helpers!
@@ -834,6 +925,8 @@
         assert s == bigint.tobytes(16, byteorder="big", signed=False)
         py.test.raises(InvalidEndiannessError, bigint.frombytes, '\xFF', 'foo',
                signed=True)
+        bigint = rbigint.frombytes('\x82', byteorder='big', signed=True)
+        assert bigint.tolong() == -126
 
     def test_tobytes(self):
         assert rbigint.fromint(0).tobytes(1, 'big', signed=True) == '\x00'


More information about the pypy-commit mailing list