Skip to content

Commit 8ebed7f

Browse files
committed
applying reviews
1 parent 3c89283 commit 8ebed7f

5 files changed

Lines changed: 125 additions & 64 deletions

File tree

‎src/csrc/umath/binary_ops.cpp‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ quad_generic_binop_strided_loop_aligned(PyArrayMethod_Context *context, char *co
132132
*(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, (Sleef_quad *)in2_ptr);
133133
}
134134
else {
135-
quad_longdouble_store(out_ptr,
136-
longdouble_op((long double *)in1_ptr, (long double *)in2_ptr));
135+
quad_longdouble_store_aligned(
136+
out_ptr, longdouble_op((long double *)in1_ptr, (long double *)in2_ptr));
137137
}
138138

139139
in1_ptr += in1_stride;
@@ -269,8 +269,8 @@ quad_generic_binop_2out_strided_loop_aligned(PyArrayMethod_Context *context, cha
269269
else {
270270
long double out1, out2;
271271
longdouble_op((long double *)in1_ptr, (long double *)in2_ptr, &out1, &out2);
272-
quad_longdouble_store(out1_ptr, out1);
273-
quad_longdouble_store(out2_ptr, out2);
272+
quad_longdouble_store_aligned(out1_ptr, out1);
273+
quad_longdouble_store_aligned(out2_ptr, out2);
274274
}
275275

276276
in1_ptr += in1_stride;
@@ -384,7 +384,8 @@ quad_ldexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data
384384
if (backend == BACKEND_SLEEF) {
385385
*(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, &exp_value);
386386
} else {
387-
quad_longdouble_store(out_ptr, longdouble_op((long double *)in1_ptr, &exp_value));
387+
quad_longdouble_store_aligned(
388+
out_ptr, longdouble_op((long double *)in1_ptr, &exp_value));
388389
}
389390

390391
in1_ptr += in1_stride;

‎src/csrc/umath/unary_ops.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ quad_generic_unary_op_strided_loop_aligned(PyArrayMethod_Context *context, char
9898
*(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in_ptr);
9999
}
100100
else {
101-
quad_longdouble_store(out_ptr, longdouble_op((long double *)in_ptr));
101+
quad_longdouble_store_aligned(out_ptr, longdouble_op((long double *)in_ptr));
102102
}
103103
in_ptr += in_stride;
104104
out_ptr += out_stride;
@@ -363,8 +363,8 @@ quad_generic_unary_op_2out_strided_loop_aligned(PyArrayMethod_Context *context,
363363
else {
364364
long double out1, out2;
365365
longdouble_op((long double *)in_ptr, &out1, &out2);
366-
quad_longdouble_store(out1_ptr, out1);
367-
quad_longdouble_store(out2_ptr, out2);
366+
quad_longdouble_store_aligned(out1_ptr, out1);
367+
quad_longdouble_store_aligned(out2_ptr, out2);
368368
}
369369
in_ptr += in_stride;
370370
out1_ptr += out1_stride;
@@ -517,7 +517,7 @@ quad_frexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data
517517
}
518518
else {
519519
long double mantissa = longdouble_op((long double *)in_ptr, &out_exp);
520-
quad_longdouble_store(out_mantissa_ptr, mantissa);
520+
quad_longdouble_store_aligned(out_mantissa_ptr, mantissa);
521521
}
522522
memcpy(out_exp_ptr, &out_exp, sizeof(int));
523523

‎src/include/quad_common.h‎

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
#ifndef _QUADDTYPE_COMMON_H
22
#define _QUADDTYPE_COMMON_H
33

4-
#ifdef __cplusplus
5-
extern "C" {
6-
#endif
7-
8-
#include <sleef.h>
9-
#include <sleefquad.h>
104
#include <float.h>
115
#include <stddef.h>
126
#include <string.h>
137

8+
#include <sleef.h>
9+
#include <sleefquad.h>
10+
1411
#ifdef _MSC_VER
1512
#include <intrin.h>
1613
#endif
1714

15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
#define X87_LONG_DOUBLE_VALUE_BYTES 10
20+
1821
typedef enum {
1922
BACKEND_INVALID = -1,
2023
BACKEND_SLEEF,
@@ -44,11 +47,39 @@ quad_value_zero(quad_value *value)
4447
#endif
4548
}
4649

50+
static inline void
51+
quad_longdouble_store(void *dst, long double value)
52+
{
53+
#if (defined(__i386__) || defined(__x86_64__)) && LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
54+
// x87 extended precision occupies bytes 0..9; the remaining bytes are padding.
55+
memset(dst, 0, sizeof(value));
56+
memcpy(dst, &value, X87_LONG_DOUBLE_VALUE_BYTES);
57+
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
58+
quad_value canonical;
59+
quad_value_zero(&canonical);
60+
canonical.longdouble_value = value;
61+
memcpy(dst, &canonical, sizeof(canonical.longdouble_value));
62+
#else
63+
memcpy(dst, &value, sizeof(value));
64+
#endif
65+
}
66+
67+
static inline void
68+
quad_longdouble_store_aligned(void *dst, long double value)
69+
{
70+
#if (defined(__i386__) || defined(__x86_64__)) && LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
71+
*(long double *)dst = value;
72+
memset((unsigned char *)dst + X87_LONG_DOUBLE_VALUE_BYTES, 0,
73+
sizeof(value) - X87_LONG_DOUBLE_VALUE_BYTES);
74+
#else
75+
quad_longdouble_store(dst, value);
76+
#endif
77+
}
78+
4779
static inline void
4880
quad_value_set_longdouble(quad_value *value, long double input)
4981
{
50-
quad_value_zero(value);
51-
value->longdouble_value = input;
82+
quad_longdouble_store_aligned(&value->longdouble_value, input);
5283
}
5384

5485
static inline void
@@ -76,29 +107,24 @@ quad_value_load_canonical(quad_value *value, const void *src, QuadBackendType ba
76107
}
77108

78109
static inline void
79-
quad_longdouble_store(void *dst, long double value)
110+
quad_value_store(void *dst, const quad_value *value, QuadBackendType backend)
80111
{
81-
#if (defined(__i386__) || defined(__x86_64__)) && LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
82-
// x87 extended precision occupies bytes 0..9; the remaining bytes are padding.
83-
memset(dst, 0, sizeof(value));
84-
memcpy(dst, &value, 10);
85-
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
86-
quad_value canonical;
87-
quad_value_set_longdouble(&canonical, value);
88-
memcpy(dst, &canonical, sizeof(canonical.longdouble_value));
89-
#else
90-
memcpy(dst, &value, sizeof(value));
91-
#endif
112+
if (backend == BACKEND_SLEEF) {
113+
memcpy(dst, &value->sleef_value, sizeof(value->sleef_value));
114+
}
115+
else {
116+
quad_longdouble_store(dst, value->longdouble_value);
117+
}
92118
}
93119

94120
static inline void
95-
quad_value_store(void *dst, const quad_value *value, QuadBackendType backend)
121+
quad_value_store_aligned(void *dst, const quad_value *value, QuadBackendType backend)
96122
{
97123
if (backend == BACKEND_SLEEF) {
98124
memcpy(dst, &value->sleef_value, sizeof(value->sleef_value));
99125
}
100126
else {
101-
quad_longdouble_store(dst, value->longdouble_value);
127+
quad_longdouble_store_aligned(dst, value->longdouble_value);
102128
}
103129
}
104130

‎src/include/utilities.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ template <bool Aligned>
7171
static inline void
7272
store_quad(char *ptr, const quad_value *val, QuadBackendType backend)
7373
{
74-
quad_value_store(ptr, val, backend);
74+
if constexpr (Aligned) {
75+
quad_value_store_aligned(ptr, val, backend);
76+
}
77+
else {
78+
quad_value_store(ptr, val, backend);
79+
}
7580
}
7681

7782
#endif // __cplusplus

‎tests/test_longdouble_padding.py‎

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,72 @@
1+
import ctypes
2+
13
import numpy as np
24
import pytest
35

46
from numpy_quaddtype import QuadPrecDType, QuadPrecision
57
from 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+
814
def 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

2135
def 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

2544
def 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

Comments
 (0)