diff --git a/h5py/h5t.pyx b/h5py/h5t.pyx index 7d1d7a1..30746f9 100644 --- a/h5py/h5t.pyx +++ b/h5py/h5t.pyx @@ -231,6 +231,14 @@ IEEE_F128LE = IEEE_F128BE.copy() IEEE_F128LE.set_order(H5T_ORDER_LE) IEEE_F128LE.lock() +LDOUBLE_LE = NATIVE_LDOUBLE.copy() +LDOUBLE_LE.set_order(H5T_ORDER_LE) +LDOUBLE_LE.lock() + +LDOUBLE_BE = NATIVE_LDOUBLE.copy() +LDOUBLE_BE.set_order(H5T_ORDER_BE) +LDOUBLE_BE.lock() + # Custom Python object pointer type cdef hid_t H5PY_OBJ = H5Tcreate(H5T_OPAQUE, sizeof(PyObject*)) H5Tset_tag(H5PY_OBJ, "PYTHON:OBJECT") @@ -294,6 +302,29 @@ available_ftypes = _DeprecatedMapping(available_ftypes, "h5py. See https://github.com/h5py/h5py/pull/926 for details.") ) + +cdef (int, int, int) _correct_float_info(ftype_, finfo): + nmant = finfo.nmant + maxexp = finfo.maxexp + minexp = finfo.minexp + # workaround for numpy's buggy finfo on float128 on ppc64 archs + if ftype_ == np.longdouble and MACHINE == 'ppc64': + # values reported by hdf5 + nmant = 116 + maxexp = 1024 + minexp = -1022 + elif ftype_ == np.longdouble and MACHINE == 'ppc64le': + # values reported by hdf5 + nmant = 52 + maxexp = 1024 + minexp = -1022 + elif nmant == 63 and finfo.nexp == 15: + # This is an 80-bit float, correct mantissa size + nmant += 1 + + return nmant, maxexp, minexp + + # === General datatype operations ============================================= @with_phil @@ -1025,23 +1056,7 @@ cdef class TypeFloatID(TypeAtomicID): # Handle non-standard exponent and mantissa sizes. for ftype_, finfo, size in _available_ftypes: - nmant = finfo.nmant - maxexp = finfo.maxexp - minexp = finfo.minexp - # workaround for numpy's buggy finfo on float128 on ppc64 archs - if ftype_ == np.longdouble and MACHINE == 'ppc64': - # values reported by hdf5 - nmant = 116 - maxexp = 1024 - minexp = -1022 - elif ftype_ == np.longdouble and MACHINE == 'ppc64le': - # values reported by hdf5 - nmant = 52 - maxexp = 1024 - minexp = -1022 - elif nmant == 63 and finfo.nexp == 15: - # This is an 80-bit float, correct mantissa size - nmant += 1 + nmant, maxexp, minexp = _correct_float_info(ftype_, finfo) if (size >= self.get_size() and m_size <= nmant and (2**e_size - e_bias - 1) <= maxexp and (1 - e_bias) >= minexp): new_dtype = np.dtype(ftype_).newbyteorder(order) @@ -1353,29 +1368,30 @@ cdef class TypeEnumID(TypeCompositeID): def _get_float_dtype_to_hdf5(): float_le = {} float_be = {} - h5_be_list = [IEEE_F16BE, IEEE_F32BE, IEEE_F64BE, IEEE_F128BE] - h5_le_list = [IEEE_F16LE, IEEE_F32LE, IEEE_F64LE, IEEE_F128LE] + h5_be_list = [IEEE_F16BE, IEEE_F32BE, IEEE_F64BE, IEEE_F128BE, + LDOUBLE_BE] + h5_le_list = [IEEE_F16LE, IEEE_F32LE, IEEE_F64LE, IEEE_F128LE, + LDOUBLE_LE] for ftype_, finfo, size in _available_ftypes: + nmant, maxexp, minexp = _correct_float_info(ftype_, finfo) for h5type in h5_be_list: spos, epos, esize, mpos, msize = h5type.get_fields() ebias = h5type.get_ebias() - if (finfo.iexp == esize and finfo.nmant == msize and - (finfo.maxexp - 1) == ebias + if (finfo.iexp == esize and nmant == msize and + (maxexp - 1) == ebias ): float_be[ftype_] = h5type for h5type in h5_le_list: spos, epos, esize, mpos, msize = h5type.get_fields() ebias = h5type.get_ebias() - if (finfo.iexp == esize and finfo.nmant == msize and - (finfo.maxexp - 1) == ebias + if (finfo.iexp == esize and nmant == msize and + (maxexp - 1) == ebias ): float_le[ftype_] = h5type if ORDER_NATIVE == H5T_ORDER_LE: float_nt = dict(float_le) else: float_nt = dict(float_be) - if np.longdouble not in float_nt: - float_nt[np.longdouble] = NATIVE_LDOUBLE return float_le, float_be, float_nt cdef dict _float_le diff --git a/h5py/tests/hl/test_datatype.py b/h5py/tests/hl/test_datatype.py index 51bdcb2..e718285 100644 --- a/h5py/tests/hl/test_datatype.py +++ b/h5py/tests/hl/test_datatype.py @@ -179,7 +179,6 @@ class TestOffsets(TestCase): with h5py.File(fname, 'r') as f: self.assertArrayEqual(f['data'], data) - def test_out_of_order_offsets(self): dt = np.dtype({ 'names' : ['f1', 'f2', 'f3'], @@ -198,3 +197,26 @@ class TestOffsets(TestCase): with h5py.File(fname, 'r') as fd: self.assertArrayEqual(fd['data'], data) + + def test_float_round_tripping(self): + dtypes = set(f for f in np.typeDict.values() + if (np.issubdtype(f, np.floating) or + np.issubdtype(f, np.complexfloating)) + ) + + dtype_dset_map = {str(j): d + for j, d in enumerate(dtypes)} + + fname = self.mktemp() + + with h5py.File(fname, 'w') as f: + for n, d in dtype_dset_map.items(): + data = np.arange(10, + dtype=d) + + f.create_dataset(n, data=data) + + with h5py.File(fname, 'r') as f: + for n, d in dtype_dset_map.items(): + ldata = f[n][:] + self.assertEqual(ldata.dtype, d)