On x86/x86-64, an 80-bit long double occupies a 16-byte dtype slot but stores only 10 meaningful bytes. Several ndarray ufunc loops write the value without initializing the remaining six bytes.
Reproducer:
import numpy as np
from numpy_quaddtype import QuadPrecDType
dtype = QuadPrecDType(backend="longdouble")
storage = bytearray(b"\xa5" * dtype.itemsize)
out = np.ndarray((1,), dtype=dtype, buffer=storage)
a = np.array([1], dtype=dtype)
b = np.array([2], dtype=dtype)
np.add(a, b, out=out)
assert float(out[0]) == 3
print(storage[10:].hex()) # a5a5a5a5a5a5
np.multiply and unary operations such as np.negative behave similarly. Newly allocated output may therefore expose allocator/stack data through tobytes(), pickling, or other raw serialization.
QuadPrecision_raw_new() already clears scalar storage, but ndarray loops write directly into raw buffers and do not construct QuadPrecisionObject instances.
We should introduce shared canonical quad_value initialization/load/store helpers and use them in scalar construction, setitem, casts, and all ufunc loops.
The zeroing operation must not be optimized away; a plain memset of a temporary was observed to disappear under optimization.
On x86/x86-64, an 80-bit
long doubleoccupies a 16-byte dtype slot but stores only 10 meaningful bytes. Several ndarray ufunc loops write the value without initializing the remaining six bytes.Reproducer:
np.multiplyand unary operations such asnp.negativebehave similarly. Newly allocated output may therefore expose allocator/stack data throughtobytes(), pickling, or other raw serialization.QuadPrecision_raw_new()already clears scalar storage, but ndarray loops write directly into raw buffers and do not constructQuadPrecisionObjectinstances.We should introduce shared canonical
quad_valueinitialization/load/store helpers and use them in scalar construction,setitem, casts, and all ufunc loops.The zeroing operation must not be optimized away; a plain
memsetof a temporary was observed to disappear under optimization.