1+ import ctypes
2+
13import numpy as np
24import pytest
35
46from numpy_quaddtype import QuadPrecDType , QuadPrecision
57from numpy_quaddtype ._quaddtype_main import from_raw_bytes
68
79
10+ X87_LONG_DOUBLE_VALUE_BYTES = 10
11+ X87_LONG_DOUBLE_STORAGE_BYTES = 16
12+
13+
814def x87_longdouble_dtype ():
915 dtype = QuadPrecDType (backend = "longdouble" )
10- if dtype .itemsize != 16 or np .finfo (np .longdouble ).nmant != 63 :
16+ if (
17+ dtype .itemsize != X87_LONG_DOUBLE_STORAGE_BYTES
18+ or np .finfo (np .longdouble ).nmant != 63
19+ ):
1120 pytest .skip ("long double is not x87 80-bit stored in 16 bytes" )
1221 return dtype
1322
1423
15- def poisoned_array (dtype , offset = 0 ):
16- storage = bytearray (b"\xa5 " * (offset + dtype .itemsize ))
24+ def poisoned_array (dtype , aligned ):
25+ storage = bytearray (b"\xa5 " * (dtype .itemsize + dtype .alignment ))
26+ address = ctypes .addressof (ctypes .c_char .from_buffer (storage ))
27+ offset = (- address ) % dtype .alignment
28+ if not aligned :
29+ offset += 1
1730 array = np .ndarray ((1 ,), dtype = dtype , buffer = storage , offset = offset )
18- return array , storage
31+ assert array .flags .aligned == aligned
32+ return array , storage , offset
1933
2034
2135def assert_zero_padding (storage , offset = 0 ):
22- assert bytes (storage [offset + 10 : offset + 16 ]) == b"\x00 " * 6
36+ padding = storage [
37+ offset + X87_LONG_DOUBLE_VALUE_BYTES : offset + X87_LONG_DOUBLE_STORAGE_BYTES
38+ ]
39+ assert bytes (padding ) == b"\x00 " * (
40+ X87_LONG_DOUBLE_STORAGE_BYTES - X87_LONG_DOUBLE_VALUE_BYTES
41+ )
2342
2443
2544def assert_array_padding_zero (array ):
2645 raw = array .tobytes ()
2746 for start in range (0 , len (raw ), array .dtype .itemsize ):
28- assert raw [start + 10 : start + 16 ] == b"\x00 " * 6
47+ assert_zero_padding (raw , start )
48+
2949
50+ def test_scalar_construction_zeroes_longdouble_padding ():
51+ x87_longdouble_dtype ()
52+
53+ value = QuadPrecision ("1.5" , backend = "longdouble" )
54+
55+ assert_zero_padding (value .__reduce__ ()[1 ][0 ])
3056
31- @pytest .mark .parametrize ("offset" , [0 , 1 ])
32- def test_setitem_zeroes_longdouble_padding (offset ):
57+
58+ @pytest .mark .parametrize ("aligned" , [True , False ])
59+ def test_setitem_zeroes_longdouble_padding (aligned ):
3360 dtype = x87_longdouble_dtype ()
34- array , storage = poisoned_array (dtype , offset )
61+ array , storage , offset = poisoned_array (dtype , aligned )
3562
3663 array [0 ] = QuadPrecision ("1.5" , backend = "longdouble" )
3764
3865 assert array [0 ] == QuadPrecision ("1.5" , backend = "longdouble" )
3966 assert_zero_padding (storage , offset )
4067
4168
42- @pytest .mark .parametrize ("offset " , [0 , 1 ])
69+ @pytest .mark .parametrize ("aligned " , [True , False ])
4370@pytest .mark .parametrize (
4471 ("operation" , "expected" ),
4572 [
@@ -48,53 +75,53 @@ def test_setitem_zeroes_longdouble_padding(offset):
4875 (lambda value , out : np .ldexp (value , 2 , out = out ), 6.0 ),
4976 ],
5077)
51- def test_ufunc_zeroes_longdouble_padding (offset , operation , expected ):
78+ def test_ufunc_zeroes_longdouble_padding (aligned , operation , expected ):
5279 dtype = x87_longdouble_dtype ()
5380 value = np .array ([1.5 ], dtype = dtype )
54- out , storage = poisoned_array (dtype , offset )
81+ out , storage , offset = poisoned_array (dtype , aligned )
5582
5683 operation (value , out )
5784
5885 assert out [0 ] == QuadPrecision (str (expected ), backend = "longdouble" )
5986 assert_zero_padding (storage , offset )
6087
6188
62- @pytest .mark .parametrize ("offset " , [0 , 1 ])
63- def test_multi_output_ufunc_zeroes_longdouble_padding (offset ):
89+ @pytest .mark .parametrize ("aligned " , [True , False ])
90+ def test_multi_output_ufunc_zeroes_longdouble_padding (aligned ):
6491 dtype = x87_longdouble_dtype ()
6592 value = np .array ([1.5 ], dtype = dtype )
66- fractional , fractional_storage = poisoned_array (dtype , offset )
67- integral , integral_storage = poisoned_array (dtype , offset )
93+ fractional , fractional_storage , fractional_offset = poisoned_array (dtype , aligned )
94+ integral , integral_storage , integral_offset = poisoned_array (dtype , aligned )
6895
6996 np .modf (value , out = (fractional , integral ))
7097
7198 assert fractional [0 ] == QuadPrecision ("0.5" , backend = "longdouble" )
7299 assert integral [0 ] == QuadPrecision ("1.0" , backend = "longdouble" )
73- assert_zero_padding (fractional_storage , offset )
74- assert_zero_padding (integral_storage , offset )
100+ assert_zero_padding (fractional_storage , fractional_offset )
101+ assert_zero_padding (integral_storage , integral_offset )
75102
76103
77- @pytest .mark .parametrize ("offset " , [0 , 1 ])
78- def test_binary_multi_output_ufunc_zeroes_longdouble_padding (offset ):
104+ @pytest .mark .parametrize ("aligned " , [True , False ])
105+ def test_binary_multi_output_ufunc_zeroes_longdouble_padding (aligned ):
79106 dtype = x87_longdouble_dtype ()
80107 dividend = np .array ([5.5 ], dtype = dtype )
81108 divisor = np .array ([2.0 ], dtype = dtype )
82- quotient , quotient_storage = poisoned_array (dtype , offset )
83- remainder , remainder_storage = poisoned_array (dtype , offset )
109+ quotient , quotient_storage , quotient_offset = poisoned_array (dtype , aligned )
110+ remainder , remainder_storage , remainder_offset = poisoned_array (dtype , aligned )
84111
85112 np .divmod (dividend , divisor , out = (quotient , remainder ))
86113
87114 assert quotient [0 ] == QuadPrecision ("2.0" , backend = "longdouble" )
88115 assert remainder [0 ] == QuadPrecision ("1.5" , backend = "longdouble" )
89- assert_zero_padding (quotient_storage , offset )
90- assert_zero_padding (remainder_storage , offset )
116+ assert_zero_padding (quotient_storage , quotient_offset )
117+ assert_zero_padding (remainder_storage , remainder_offset )
91118
92119
93- @pytest .mark .parametrize ("offset " , [0 , 1 ])
94- def test_frexp_zeroes_longdouble_padding (offset ):
120+ @pytest .mark .parametrize ("aligned " , [True , False ])
121+ def test_frexp_zeroes_longdouble_padding (aligned ):
95122 dtype = x87_longdouble_dtype ()
96123 value = np .array ([6.0 ], dtype = dtype )
97- mantissa , storage = poisoned_array (dtype , offset )
124+ mantissa , storage , offset = poisoned_array (dtype , aligned )
98125 exponent = np .empty (1 , dtype = np .int32 )
99126
100127 np .frexp (value , out = (mantissa , exponent ))
@@ -104,9 +131,9 @@ def test_frexp_zeroes_longdouble_padding(offset):
104131 assert_zero_padding (storage , offset )
105132
106133
107- @pytest .mark .parametrize ("offset " , [0 , 1 ])
134+ @pytest .mark .parametrize ("aligned " , [True , False ])
108135@pytest .mark .parametrize ("source_backend" , [None , "sleef" ])
109- def test_cast_zeroes_longdouble_padding (offset , source_backend ):
136+ def test_cast_zeroes_longdouble_padding (aligned , source_backend ):
110137 dtype = x87_longdouble_dtype ()
111138 if source_backend is None :
112139 source = np .array ([1.5 ], dtype = np .float64 )
@@ -115,7 +142,7 @@ def test_cast_zeroes_longdouble_padding(offset, source_backend):
115142 [QuadPrecision ("1.5" , backend = source_backend )],
116143 dtype = QuadPrecDType (backend = source_backend ),
117144 )
118- out , storage = poisoned_array (dtype , offset )
145+ out , storage , offset = poisoned_array (dtype , aligned )
119146
120147 np .copyto (out , source , casting = "unsafe" )
121148
@@ -150,10 +177,12 @@ def test_from_raw_bytes_zeroes_longdouble_padding():
150177 x87_longdouble_dtype ()
151178 original = QuadPrecision ("1.5" , backend = "longdouble" )
152179 raw = bytearray (original .__reduce__ ()[1 ][0 ])
153- raw [10 :] = b"\xa5 " * 6
180+ raw [X87_LONG_DOUBLE_VALUE_BYTES :] = b"\xa5 " * (
181+ X87_LONG_DOUBLE_STORAGE_BYTES - X87_LONG_DOUBLE_VALUE_BYTES
182+ )
154183
155184 result = from_raw_bytes (bytes (raw ), "longdouble" )
156185 result_raw = result .__reduce__ ()[1 ][0 ]
157186
158187 assert result == original
159- assert result_raw [ 10 :] == b" \x00 " * 6
188+ assert_zero_padding ( result_raw )
0 commit comments