[pypy-commit] pypy default: hg merge int-float-list-strategy
arigo
noreply at buildbot.pypy.org
Sat Jul 4 11:19:34 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r78425:d369501b8f6d
Date: 2015-07-04 11:19 +0200
http://bitbucket.org/pypy/pypy/changeset/d369501b8f6d/
Log: hg merge int-float-list-strategy
Add a list strategy for lists that store both floats and 32-bit
integers. The latter are encoded as nonstandard NaNs. Benchmarks
show that the speed of such lists is now very close to the speed of
purely-int or purely-float lists.
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -14,6 +14,7 @@
from rpython.rlib.listsort import make_timsort_class
from rpython.rlib.objectmodel import (
import_from_mixin, instantiate, newlist_hint, resizelist_hint, specialize)
+from rpython.rlib import longlong2float
from rpython.tool.sourcetools import func_with_new_name
from pypy.interpreter.baseobjspace import W_Root
@@ -73,33 +74,56 @@
return SizeListStrategy(space, sizehint)
return space.fromcache(EmptyListStrategy)
- # check for ints
- for w_obj in list_w:
- if not type(w_obj) is W_IntObject:
+ w_firstobj = list_w[0]
+ check_int_or_float = False
+
+ if type(w_firstobj) is W_IntObject:
+ # check for all-ints
+ for i in range(1, len(list_w)):
+ w_obj = list_w[i]
+ if type(w_obj) is not W_IntObject:
+ check_int_or_float = (type(w_obj) is W_FloatObject)
+ break
+ else:
+ return space.fromcache(IntegerListStrategy)
+
+ elif type(w_firstobj) is W_BytesObject:
+ # check for all-strings
+ for i in range(1, len(list_w)):
+ if type(list_w[i]) is not W_BytesObject:
+ break
+ else:
+ return space.fromcache(BytesListStrategy)
+
+ elif type(w_firstobj) is W_UnicodeObject:
+ # check for all-unicodes
+ for i in range(1, len(list_w)):
+ if type(list_w[i]) is not W_UnicodeObject:
+ break
+ else:
+ return space.fromcache(UnicodeListStrategy)
+
+ elif type(w_firstobj) is W_FloatObject:
+ # check for all-floats
+ for i in range(1, len(list_w)):
+ w_obj = list_w[i]
+ if type(w_obj) is not W_FloatObject:
+ check_int_or_float = (type(w_obj) is W_IntObject)
+ break
+ else:
+ return space.fromcache(FloatListStrategy)
+
+ if check_int_or_float:
+ for w_obj in list_w:
+ if type(w_obj) is W_IntObject:
+ if longlong2float.can_encode_int32(space.int_w(w_obj)):
+ continue # ok
+ elif type(w_obj) is W_FloatObject:
+ if longlong2float.can_encode_float(space.float_w(w_obj)):
+ continue # ok
break
- else:
- return space.fromcache(IntegerListStrategy)
-
- # check for strings
- for w_obj in list_w:
- if not type(w_obj) is W_BytesObject:
- break
- else:
- return space.fromcache(BytesListStrategy)
-
- # check for unicode
- for w_obj in list_w:
- if not type(w_obj) is W_UnicodeObject:
- break
- else:
- return space.fromcache(UnicodeListStrategy)
-
- # check for floats
- for w_obj in list_w:
- if not type(w_obj) is W_FloatObject:
- break
- else:
- return space.fromcache(FloatListStrategy)
+ else:
+ return space.fromcache(IntOrFloatListStrategy)
return space.fromcache(ObjectListStrategy)
@@ -1382,12 +1406,15 @@
return W_ListObject.from_storage_and_strategy(
self.space, storage, self)
+ def switch_to_next_strategy(self, w_list, w_sample_item):
+ w_list.switch_to_object_strategy()
+
def append(self, w_list, w_item):
if self.is_correct_type(w_item):
self.unerase(w_list.lstorage).append(self.unwrap(w_item))
return
- w_list.switch_to_object_strategy()
+ self.switch_to_next_strategy(w_list, w_item)
w_list.append(w_item)
def insert(self, w_list, index, w_item):
@@ -1397,7 +1424,7 @@
l.insert(index, self.unwrap(w_item))
return
- w_list.switch_to_object_strategy()
+ self.switch_to_next_strategy(w_list, w_item)
w_list.insert(index, w_item)
def _extend_from_list(self, w_list, w_other):
@@ -1421,7 +1448,7 @@
except IndexError:
raise
else:
- w_list.switch_to_object_strategy()
+ self.switch_to_next_strategy(w_list, w_item)
w_list.setitem(index, w_item)
def setslice(self, w_list, start, step, slicelength, w_other):
@@ -1586,6 +1613,9 @@
def getitems(self, w_list):
return self.unerase(w_list.lstorage)
+ # no sort() method here: W_ListObject.descr_sort() handles this
+ # case explicitly
+
class IntegerListStrategy(ListStrategy):
import_from_mixin(AbstractUnwrappedStrategy)
@@ -1628,6 +1658,11 @@
assert other is not None
l += other
return
+ if (w_other.strategy is self.space.fromcache(FloatListStrategy) or
+ w_other.strategy is self.space.fromcache(IntOrFloatListStrategy)):
+ if self.switch_to_int_or_float_strategy(w_list):
+ w_list.extend(w_other)
+ return
return self._base_extend_from_list(w_list, w_other)
@@ -1638,8 +1673,46 @@
storage = self.erase(w_other.getitems_int())
w_other = W_ListObject.from_storage_and_strategy(
self.space, storage, self)
+ if (w_other.strategy is self.space.fromcache(FloatListStrategy) or
+ w_other.strategy is self.space.fromcache(IntOrFloatListStrategy)):
+ if self.switch_to_int_or_float_strategy(w_list):
+ w_list.setslice(start, step, slicelength, w_other)
+ return
return self._base_setslice(w_list, start, step, slicelength, w_other)
+
+ @staticmethod
+ def int_2_float_or_int(w_list):
+ l = IntegerListStrategy.unerase(w_list.lstorage)
+ if not longlong2float.CAN_ALWAYS_ENCODE_INT32:
+ for intval in l:
+ if not longlong2float.can_encode_int32(intval):
+ raise ValueError
+ return [longlong2float.encode_int32_into_longlong_nan(intval)
+ for intval in l]
+
+ def switch_to_int_or_float_strategy(self, w_list):
+ try:
+ generalized_list = self.int_2_float_or_int(w_list)
+ except ValueError:
+ return False
+ strategy = self.space.fromcache(IntOrFloatListStrategy)
+ w_list.strategy = strategy
+ w_list.lstorage = strategy.erase(generalized_list)
+ return True
+
+ def switch_to_next_strategy(self, w_list, w_sample_item):
+ if type(w_sample_item) is W_FloatObject:
+ if self.switch_to_int_or_float_strategy(w_list):
+ # yes, we can switch to IntOrFloatListStrategy
+ # (ignore here the extremely unlikely case where
+ # w_sample_item is just the wrong nonstandard NaN float;
+ # it will caught later and yet another switch will occur)
+ return
+ # no, fall back to ObjectListStrategy
+ w_list.switch_to_object_strategy()
+
+
class FloatListStrategy(ListStrategy):
import_from_mixin(AbstractUnwrappedStrategy)
@@ -1671,9 +1744,34 @@
def getitems_float(self, w_list):
return self.unerase(w_list.lstorage)
+
+ _base_extend_from_list = _extend_from_list
+
+ def _extend_from_list(self, w_list, w_other):
+ if (w_other.strategy is self.space.fromcache(IntegerListStrategy) or
+ w_other.strategy is self.space.fromcache(IntOrFloatListStrategy)):
+ # xxx a case that we don't optimize: [3.4].extend([9999999999999])
+ # will cause a switch to int-or-float, followed by another
+ # switch to object
+ if self.switch_to_int_or_float_strategy(w_list):
+ w_list.extend(w_other)
+ return
+ return self._base_extend_from_list(w_list, w_other)
+
+
+ _base_setslice = setslice
+
+ def setslice(self, w_list, start, step, slicelength, w_other):
+ if (w_other.strategy is self.space.fromcache(IntegerListStrategy) or
+ w_other.strategy is self.space.fromcache(IntOrFloatListStrategy)):
+ if self.switch_to_int_or_float_strategy(w_list):
+ w_list.setslice(start, step, slicelength, w_other)
+ return
+ return self._base_setslice(w_list, start, step, slicelength, w_other)
+
+
def _safe_find(self, w_list, obj, start, stop):
from rpython.rlib.rfloat import isnan
- from rpython.rlib.longlong2float import float2longlong
#
l = self.unerase(w_list.lstorage)
stop = min(stop, len(l))
@@ -1683,13 +1781,156 @@
if val == obj:
return i
else:
- search = float2longlong(obj)
+ search = longlong2float.float2longlong(obj)
for i in range(start, stop):
val = l[i]
- if float2longlong(val) == search:
+ if longlong2float.float2longlong(val) == search:
return i
raise ValueError
+ @staticmethod
+ def float_2_float_or_int(w_list):
+ l = FloatListStrategy.unerase(w_list.lstorage)
+ generalized_list = []
+ for floatval in l:
+ if not longlong2float.can_encode_float(floatval):
+ raise ValueError
+ generalized_list.append(
+ longlong2float.float2longlong(floatval))
+ return generalized_list
+
+ def switch_to_int_or_float_strategy(self, w_list):
+ # xxx we should be able to use the same lstorage, but
+ # there is a typing issue (float vs longlong)...
+ try:
+ generalized_list = self.float_2_float_or_int(w_list)
+ except ValueError:
+ return False
+ strategy = self.space.fromcache(IntOrFloatListStrategy)
+ w_list.strategy = strategy
+ w_list.lstorage = strategy.erase(generalized_list)
+ return True
+
+ def switch_to_next_strategy(self, w_list, w_sample_item):
+ if type(w_sample_item) is W_IntObject:
+ sample_intval = self.space.int_w(w_sample_item)
+ if longlong2float.can_encode_int32(sample_intval):
+ if self.switch_to_int_or_float_strategy(w_list):
+ # yes, we can switch to IntOrFloatListStrategy
+ return
+ # no, fall back to ObjectListStrategy
+ w_list.switch_to_object_strategy()
+
+
+class IntOrFloatListStrategy(ListStrategy):
+ import_from_mixin(AbstractUnwrappedStrategy)
+
+ _none_value = longlong2float.float2longlong(0.0)
+
+ def wrap(self, llval):
+ if longlong2float.is_int32_from_longlong_nan(llval):
+ intval = longlong2float.decode_int32_from_longlong_nan(llval)
+ return self.space.wrap(intval)
+ else:
+ floatval = longlong2float.longlong2float(llval)
+ return self.space.wrap(floatval)
+
+ def unwrap(self, w_int_or_float):
+ if type(w_int_or_float) is W_IntObject:
+ intval = self.space.int_w(w_int_or_float)
+ return longlong2float.encode_int32_into_longlong_nan(intval)
+ else:
+ floatval = self.space.float_w(w_int_or_float)
+ return longlong2float.float2longlong(floatval)
+
+ erase, unerase = rerased.new_erasing_pair("longlong")
+ erase = staticmethod(erase)
+ unerase = staticmethod(unerase)
+
+ def is_correct_type(self, w_obj):
+ if type(w_obj) is W_IntObject:
+ intval = self.space.int_w(w_obj)
+ return longlong2float.can_encode_int32(intval)
+ elif type(w_obj) is W_FloatObject:
+ floatval = self.space.float_w(w_obj)
+ return longlong2float.can_encode_float(floatval)
+ else:
+ return False
+
+ def list_is_correct_type(self, w_list):
+ return w_list.strategy is self.space.fromcache(IntOrFloatListStrategy)
+
+ def sort(self, w_list, reverse):
+ l = self.unerase(w_list.lstorage)
+ sorter = IntOrFloatSort(l, len(l))
+ # Reverse sort stability achieved by initially reversing the list,
+ # applying a stable forward sort, then reversing the final result.
+ if reverse:
+ l.reverse()
+ sorter.sort()
+ if reverse:
+ l.reverse()
+
+ _base_extend_from_list = _extend_from_list
+
+ def _extend_longlong(self, w_list, longlong_list):
+ l = self.unerase(w_list.lstorage)
+ l += longlong_list
+
+ def _extend_from_list(self, w_list, w_other):
+ if w_other.strategy is self.space.fromcache(IntegerListStrategy):
+ try:
+ longlong_list = IntegerListStrategy.int_2_float_or_int(w_other)
+ except ValueError:
+ pass
+ else:
+ return self._extend_longlong(w_list, longlong_list)
+ if w_other.strategy is self.space.fromcache(FloatListStrategy):
+ try:
+ longlong_list = FloatListStrategy.float_2_float_or_int(w_other)
+ except ValueError:
+ pass
+ else:
+ return self._extend_longlong(w_list, longlong_list)
+ return self._base_extend_from_list(w_list, w_other)
+
+ _base_setslice = setslice
+
+ def _temporary_longlong_list(self, longlong_list):
+ storage = self.erase(longlong_list)
+ return W_ListObject.from_storage_and_strategy(self.space, storage, self)
+
+ def setslice(self, w_list, start, step, slicelength, w_other):
+ if w_other.strategy is self.space.fromcache(IntegerListStrategy):
+ try:
+ longlong_list = IntegerListStrategy.int_2_float_or_int(w_other)
+ except ValueError:
+ pass
+ else:
+ w_other = self._temporary_longlong_list(longlong_list)
+ elif w_other.strategy is self.space.fromcache(FloatListStrategy):
+ try:
+ longlong_list = FloatListStrategy.float_2_float_or_int(w_other)
+ except ValueError:
+ pass
+ else:
+ w_other = self._temporary_longlong_list(longlong_list)
+ return self._base_setslice(w_list, start, step, slicelength, w_other)
+
+ def _safe_find(self, w_list, obj, start, stop):
+ l = self.unerase(w_list.lstorage)
+ # careful: we must consider that 0.0 == -0.0 == 0, but also
+ # NaN == NaN if they have the same bit pattern.
+ fobj = longlong2float.maybe_decode_longlong_as_float(obj)
+ for i in range(start, min(stop, len(l))):
+ llval = l[i]
+ if llval == obj: # equal as longlongs: includes NaN == NaN
+ return i
+ fval = longlong2float.maybe_decode_longlong_as_float(llval)
+ if fval == fobj: # cases like 0.0 == -0.0 or 42 == 42.0
+ return i
+ raise ValueError
+
class BytesListStrategy(ListStrategy):
import_from_mixin(AbstractUnwrappedStrategy)
@@ -1786,6 +2027,7 @@
TimSort = make_timsort_class()
IntBaseTimSort = make_timsort_class()
FloatBaseTimSort = make_timsort_class()
+IntOrFloatBaseTimSort = make_timsort_class()
StringBaseTimSort = make_timsort_class()
UnicodeBaseTimSort = make_timsort_class()
@@ -1816,6 +2058,13 @@
return a < b
+class IntOrFloatSort(IntOrFloatBaseTimSort):
+ def lt(self, a, b):
+ fa = longlong2float.maybe_decode_longlong_as_float(a)
+ fb = longlong2float.maybe_decode_longlong_as_float(b)
+ return fa < fb
+
+
class StringSort(StringBaseTimSort):
def lt(self, a, b):
return a < b
diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py
--- a/pypy/objspace/std/test/test_listobject.py
+++ b/pypy/objspace/std/test/test_listobject.py
@@ -1575,7 +1575,19 @@
L2 = [N, 0.0] # float strategy
assert N in L2
assert L2.index(N) == 0
+ assert L2.index(-0.0) == 1
assert L2 == [N, -0.0]
+ # same with the int-or-float list strategy
+ L3 = [N, 0.0, -0.0, 0]
+ assert N in L3
+ assert L3.index(N) == 0
+ for i in [1, 2, 3]:
+ assert L3[i] == 0
+ assert L3[i] == 0.0
+ assert L3[i] == -0.0
+ assert L3.index(0, i) == i
+ assert L3.index(0.0, i) == i
+ assert L3.index(-0.0, i) == i
class AppTestListObjectWithRangeList(AppTestListObject):
diff --git a/pypy/objspace/std/test/test_liststrategies.py b/pypy/objspace/std/test/test_liststrategies.py
--- a/pypy/objspace/std/test/test_liststrategies.py
+++ b/pypy/objspace/std/test/test_liststrategies.py
@@ -1,8 +1,10 @@
import sys
+import py
from pypy.objspace.std.listobject import (
W_ListObject, EmptyListStrategy, ObjectListStrategy, IntegerListStrategy,
FloatListStrategy, BytesListStrategy, RangeListStrategy,
- SimpleRangeListStrategy, make_range_list, UnicodeListStrategy)
+ SimpleRangeListStrategy, make_range_list, UnicodeListStrategy,
+ IntOrFloatListStrategy)
from pypy.objspace.std import listobject
from pypy.objspace.std.test.test_listobject import TestW_ListObject
@@ -166,7 +168,6 @@
assert isinstance(l.strategy, IntegerListStrategy)
def test_list_empty_after_delete(self):
- import py
py.test.skip("return to emptyliststrategy is not supported anymore")
l = W_ListObject(self.space, [self.space.wrap(3)])
assert isinstance(l.strategy, IntegerListStrategy)
@@ -317,7 +318,7 @@
l = W_ListObject(space, [w(1.1), w(2.2), w(3.3)])
assert isinstance(l.strategy, FloatListStrategy)
- l.extend(W_ListObject(space, [w(4), w(5), w(6)]))
+ l.extend(W_ListObject(space, [w("abc"), w("def"), w("ghi")]))
assert isinstance(l.strategy, ObjectListStrategy)
def test_empty_extend_with_any(self):
@@ -733,6 +734,306 @@
list_copy[0] = 42
assert list_orig == [1, 2, 3]
+ def test_int_or_float_special_nan(self):
+ from rpython.rlib import longlong2float, rarithmetic
+ space = self.space
+ ll = rarithmetic.r_longlong(0xfffffffe12345678 - 2**64)
+ specialnan = longlong2float.longlong2float(ll)
+ w_l = W_ListObject(space, [space.wrap(1), space.wrap(specialnan)])
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+
+ def test_int_or_float_int_overflow(self):
+ if sys.maxint == 2147483647:
+ py.test.skip("only on 64-bit")
+ space = self.space
+ ok1 = 2**31 - 1
+ ok2 = -2**31
+ ovf1 = ok1 + 1
+ ovf2 = ok2 - 1
+ w_l = W_ListObject(space, [space.wrap(1.2), space.wrap(ovf1)])
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ w_l = W_ListObject(space, [space.wrap(1.2), space.wrap(ovf2)])
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ w_l = W_ListObject(space, [space.wrap(1.2), space.wrap(ok1)])
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l = W_ListObject(space, [space.wrap(1.2), space.wrap(ok2)])
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+
+ def test_int_or_float_base(self):
+ from rpython.rlib.rfloat import INFINITY, NAN
+ space = self.space
+ w = space.wrap
+ w_l = W_ListObject(space, [space.wrap(1), space.wrap(2.3)])
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l.append(w(int(2**31-1)))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l.append(w(-5.1))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ assert space.int_w(w_l.getitem(2)) == 2**31-1
+ assert space.float_w(w_l.getitem(3)) == -5.1
+ w_l.append(w(INFINITY))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l.append(w(NAN))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l.append(w(-NAN))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ w_l.append(space.newlist([]))
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+
+ def test_int_or_float_from_integer(self):
+ space = self.space
+ w = space.wrap
+ w_l = W_ListObject(space, [space.wrap(int(-2**31))])
+ assert isinstance(w_l.strategy, IntegerListStrategy)
+ w_l.append(w(-5.1))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ assert space.int_w(w_l.getitem(0)) == -2**31
+ assert space.float_w(w_l.getitem(1)) == -5.1
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_from_integer_overflow(self):
+ if sys.maxint == 2147483647:
+ py.test.skip("only on 64-bit")
+ space = self.space
+ w = space.wrap
+ ovf1 = -2**31 - 1
+ w_l = W_ListObject(space, [space.wrap(ovf1)])
+ assert isinstance(w_l.strategy, IntegerListStrategy)
+ w_l.append(w(-5.1))
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ assert space.int_w(w_l.getitem(0)) == ovf1
+ assert space.float_w(w_l.getitem(1)) == -5.1
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_from_integer_special_nan(self):
+ from rpython.rlib import longlong2float, rarithmetic
+ space = self.space
+ w = space.wrap
+ w_l = W_ListObject(space, [space.wrap(int(-2**31))])
+ assert isinstance(w_l.strategy, IntegerListStrategy)
+ ll = rarithmetic.r_longlong(0xfffffffe12345678 - 2**64)
+ specialnan = longlong2float.longlong2float(ll)
+ w_l.append(w(specialnan))
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ assert space.int_w(w_l.getitem(0)) == -2**31
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_from_float(self):
+ space = self.space
+ w = space.wrap
+ w_l = W_ListObject(space, [space.wrap(-42.5)])
+ assert isinstance(w_l.strategy, FloatListStrategy)
+ w_l.append(w(-15))
+ assert isinstance(w_l.strategy, IntOrFloatListStrategy)
+ assert space.float_w(w_l.getitem(0)) == -42.5
+ assert space.int_w(w_l.getitem(1)) == -15
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_from_float_int_overflow(self):
+ if sys.maxint == 2147483647:
+ py.test.skip("only on 64-bit")
+ space = self.space
+ w = space.wrap
+ ovf1 = 2 ** 31
+ w_l = W_ListObject(space, [space.wrap(1.2)])
+ assert isinstance(w_l.strategy, FloatListStrategy)
+ w_l.append(w(ovf1))
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ assert space.float_w(w_l.getitem(0)) == 1.2
+ assert space.int_w(w_l.getitem(1)) == ovf1
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_from_float_special_nan(self):
+ from rpython.rlib import longlong2float, rarithmetic
+ space = self.space
+ w = space.wrap
+ ll = rarithmetic.r_longlong(0xfffffffe12345678 - 2**64)
+ specialnan = longlong2float.longlong2float(ll)
+ w_l = W_ListObject(space, [space.wrap(specialnan)])
+ assert isinstance(w_l.strategy, FloatListStrategy)
+ w_l.append(w(42))
+ assert isinstance(w_l.strategy, ObjectListStrategy)
+ assert space.int_w(w_l.getitem(1)) == 42
+ assert space.len_w(w_l) == 2
+
+ def test_int_or_float_extend(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3), space.wrap(4.5)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [0, 1.2, 3, 4.5]
+
+ def test_int_or_float_extend_mixed_1(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert [(type(x), x) for x in space.unwrap(w_l1)] == [
+ (int, 0), (float, 1.2), (int, 3)]
+
+ def test_int_or_float_extend_mixed_2(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3.4)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, FloatListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [0, 1.2, 3.4]
+
+ def test_int_or_float_extend_mixed_3(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0)])
+ w_l2 = W_ListObject(space, [space.wrap(3.4)])
+ assert isinstance(w_l1.strategy, IntegerListStrategy)
+ assert isinstance(w_l2.strategy, FloatListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [0, 3.4]
+
+ def test_int_or_float_extend_mixed_4(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0)])
+ w_l2 = W_ListObject(space, [space.wrap(3.4), space.wrap(-2)])
+ assert isinstance(w_l1.strategy, IntegerListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [0, 3.4, -2]
+
+ def test_int_or_float_extend_mixed_5(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(-2.5)])
+ w_l2 = W_ListObject(space, [space.wrap(42)])
+ assert isinstance(w_l1.strategy, FloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [-2.5, 42]
+
+ def test_int_or_float_extend_mixed_5_overflow(self):
+ if sys.maxint == 2147483647:
+ py.test.skip("only on 64-bit")
+ space = self.space
+ ovf1 = 2 ** 35
+ w_l1 = W_ListObject(space, [space.wrap(-2.5)])
+ w_l2 = W_ListObject(space, [space.wrap(ovf1)])
+ assert isinstance(w_l1.strategy, FloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, ObjectListStrategy)
+ assert space.unwrap(w_l1) == [-2.5, ovf1]
+
+ def test_int_or_float_extend_mixed_6(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(-2.5)])
+ w_l2 = W_ListObject(space, [space.wrap(3.4), space.wrap(-2)])
+ assert isinstance(w_l1.strategy, FloatListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.extend(w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [-2.5, 3.4, -2]
+
+ def test_int_or_float_setslice(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3), space.wrap(4.5)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [3, 4.5, 1.2]
+
+ def test_int_or_float_setslice_mixed_1(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(12)])
+ w_l2 = W_ListObject(space, [space.wrap(3.2), space.wrap(4.5)])
+ assert isinstance(w_l1.strategy, IntegerListStrategy)
+ assert isinstance(w_l2.strategy, FloatListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [3.2, 4.5, 12]
+
+ def test_int_or_float_setslice_mixed_2(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(12)])
+ w_l2 = W_ListObject(space, [space.wrap(3.2), space.wrap(45)])
+ assert isinstance(w_l1.strategy, IntegerListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [3.2, 45, 12]
+
+ def test_int_or_float_setslice_mixed_3(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0.1), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(32), space.wrap(45)])
+ assert isinstance(w_l1.strategy, FloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [32, 45, 1.2]
+
+ def test_int_or_float_setslice_mixed_4(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0.1), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3.2), space.wrap(45)])
+ assert isinstance(w_l1.strategy, FloatListStrategy)
+ assert isinstance(w_l2.strategy, IntOrFloatListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [3.2, 45, 1.2]
+
+ def test_int_or_float_setslice_mixed_5(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(32), space.wrap(45)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [32, 45, 1.2]
+
+ def test_int_or_float_setslice_mixed_5_overflow(self):
+ if sys.maxint == 2147483647:
+ py.test.skip("only on 64-bit")
+ space = self.space
+ ovf1 = 2 ** 35
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(32), space.wrap(ovf1)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, IntegerListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, ObjectListStrategy)
+ assert space.unwrap(w_l1) == [32, ovf1, 1.2]
+
+ def test_int_or_float_setslice_mixed_6(self):
+ space = self.space
+ w_l1 = W_ListObject(space, [space.wrap(0), space.wrap(1.2)])
+ w_l2 = W_ListObject(space, [space.wrap(3.2), space.wrap(4.5)])
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert isinstance(w_l2.strategy, FloatListStrategy)
+ w_l1.setslice(0, 1, 1, w_l2)
+ assert isinstance(w_l1.strategy, IntOrFloatListStrategy)
+ assert space.unwrap(w_l1) == [3.2, 4.5, 1.2]
+
+ def test_int_or_float_sort(self):
+ space = self.space
+ w_l = W_ListObject(space, [space.wrap(1.2), space.wrap(1),
+ space.wrap(1.0), space.wrap(5)])
+ w_l.sort(False)
+ assert [(type(x), x) for x in space.unwrap(w_l)] == [
+ (int, 1), (float, 1.0), (float, 1.2), (int, 5)]
+ w_l.sort(True)
+ assert [(type(x), x) for x in space.unwrap(w_l)] == [
+ (int, 5), (float, 1.2), (int, 1), (float, 1.0)]
+
class TestW_ListStrategiesDisabled:
spaceconfig = {"objspace.std.withliststrategies": False}
diff --git a/rpython/rlib/longlong2float.py b/rpython/rlib/longlong2float.py
--- a/rpython/rlib/longlong2float.py
+++ b/rpython/rlib/longlong2float.py
@@ -7,8 +7,9 @@
"""
from __future__ import with_statement
+import sys
from rpython.annotator import model as annmodel
-from rpython.rlib.rarithmetic import r_int64
+from rpython.rlib.rarithmetic import r_int64, intmask
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.extregistry import ExtRegistryEntry
from rpython.translator.tool.cbuild import ExternalCompilationInfo
@@ -99,3 +100,46 @@
[v_longlong] = hop.inputargs(lltype.SignedLongLong)
hop.exception_cannot_occur()
return hop.genop("convert_longlong_bytes_to_float", [v_longlong], resulttype=lltype.Float)
+
+# ____________________________________________________________
+
+
+# For encoding integers inside nonstandard NaN bit patterns.
+# ff ff ff fe xx xx xx xx (signed 32-bit int)
+nan_high_word_int32 = -2 # -2 == (int)0xfffffffe
+nan_encoded_zero = r_int64(nan_high_word_int32 << 32)
+
+def encode_int32_into_longlong_nan(value):
+ return (nan_encoded_zero +
+ rffi.cast(rffi.LONGLONG, rffi.cast(rffi.UINT, value)))
+
+def decode_int32_from_longlong_nan(value):
+ return rffi.cast(lltype.Signed, rffi.cast(rffi.INT, value))
+
+def is_int32_from_longlong_nan(value):
+ return intmask(value >> 32) == nan_high_word_int32
+
+CAN_ALWAYS_ENCODE_INT32 = (sys.maxint == 2147483647)
+
+def can_encode_int32(value):
+ if CAN_ALWAYS_ENCODE_INT32:
+ return True
+ return value == rffi.cast(lltype.Signed, rffi.cast(rffi.INT, value))
+
+def can_encode_float(value):
+ return intmask(float2longlong(value) >> 32) != nan_high_word_int32
+
+def maybe_decode_longlong_as_float(value):
+ # Decode a longlong value. If a float, just return it as a float.
+ # If an encoded integer, return a float that has the (exact) same
+ # value. This relies on the fact that casting from a decoded int
+ # to a float is an exact operation. If we had full 64-bit
+ # integers, this cast would loose precision. But this works
+ # because the integers are only 32-bit. This would also work even
+ # if we encoded larger integers: as long as they are encoded
+ # inside a subset of the mantissa of a float, then the
+ # cast-to-float will be exact.
+ if is_int32_from_longlong_nan(value):
+ return float(decode_int32_from_longlong_nan(value))
+ else:
+ return longlong2float(value)
diff --git a/rpython/rlib/test/test_longlong2float.py b/rpython/rlib/test/test_longlong2float.py
--- a/rpython/rlib/test/test_longlong2float.py
+++ b/rpython/rlib/test/test_longlong2float.py
@@ -1,7 +1,7 @@
from rpython.translator.c.test.test_genc import compile
from rpython.rlib.longlong2float import longlong2float, float2longlong
from rpython.rlib.longlong2float import uint2singlefloat, singlefloat2uint
-from rpython.rlib.rarithmetic import r_singlefloat
+from rpython.rlib.rarithmetic import r_singlefloat, r_longlong
from rpython.rtyper.test.test_llinterp import interpret
@@ -21,6 +21,9 @@
yield -inf
yield inf / inf # nan
+def test_float2longlong():
+ assert float2longlong(0.0) == r_longlong(0)
+
def test_longlong_as_float():
for x in enum_floats():
res = fn(x)
@@ -63,3 +66,32 @@
for x in enum_floats():
res = fn2(x)
assert repr(res) == repr(float(r_singlefloat(x)))
+
+# ____________________________________________________________
+
+def fn_encode_nan(f1, i2):
+ from rpython.rlib.longlong2float import can_encode_float, can_encode_int32
+ from rpython.rlib.longlong2float import encode_int32_into_longlong_nan
+ from rpython.rlib.longlong2float import decode_int32_from_longlong_nan
+ from rpython.rlib.longlong2float import is_int32_from_longlong_nan
+ from rpython.rlib.rfloat import isnan
+ assert can_encode_float(f1)
+ assert can_encode_int32(i2)
+ l1 = float2longlong(f1)
+ l2 = encode_int32_into_longlong_nan(i2)
+ assert not is_int32_from_longlong_nan(l1)
+ assert is_int32_from_longlong_nan(l2)
+ f1b = longlong2float(l1)
+ assert f1b == f1 or (isnan(f1b) and isnan(f1))
+ assert decode_int32_from_longlong_nan(l2) == i2
+ return 42
+
+def test_compiled_encode_nan():
+ fn2 = compile(fn_encode_nan, [float, int])
+ ints = [-2**31, 2**31-1, 42]
+ for x in enum_floats():
+ y = ints.pop()
+ ints.insert(0, y)
+ fn_encode_nan(x, y)
+ res = fn2(x, y)
+ assert res == 42
More information about the pypy-commit
mailing list