Skip to content

Commit

Permalink
Refactor special floating-point value handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jhendersonHDF committed Jun 18, 2024
1 parent 7ac1403 commit 88c7fc9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 128 deletions.
2 changes: 1 addition & 1 deletion src/H5Tbit.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ H5T__bit_shift(uint8_t *buf, ssize_t shift_dist, size_t offset, size_t size)
*-------------------------------------------------------------------------
*/
uint64_t
H5T__bit_get_d(uint8_t *buf, size_t offset, size_t size)
H5T__bit_get_d(const uint8_t *buf, size_t offset, size_t size)
{
uint64_t val = 0;
size_t i, hs;
Expand Down
4 changes: 3 additions & 1 deletion src/H5Tconv_bitfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ H5T__conv_b_b(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, const H5T_
}

/* Allocate space for order-reversed source buffer */
src_rev = (uint8_t *)H5MM_calloc(src->shared->size);
if (conv_ctx->u.conv.cb_struct.func)
if (NULL == (src_rev = H5MM_calloc(src->shared->size)))
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "unable to allocate temporary buffer");

/* The conversion loop */
H5_CHECK_OVERFLOW(buf_stride, size_t, ssize_t);
Expand Down
225 changes: 103 additions & 122 deletions src/H5Tconv_float.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,23 +177,24 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
dst_delta = (ssize_t)direction * (ssize_t)(buf_stride ? buf_stride : dst_p->shared->size);

/* Allocate space for order-reversed source buffer */
if (NULL == (src_rev = H5MM_calloc(src_p->shared->size)))
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer");
if (conv_ctx->u.conv.cb_struct.func)
if (NULL == (src_rev = H5MM_calloc(src_p->shared->size)))
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer");

/* The conversion loop */
for (size_t elmtno = 0; elmtno < nelmts; elmtno++) {
H5T_conv_ret_t except_ret = H5T_CONV_UNHANDLED; /* return of conversion exception callback function */
uint64_t sign; /* source sign bit value */
ssize_t bitno = 0; /* bit number */
int64_t expo; /* exponent */
uint8_t dbuf[TEMP_FLOAT_CONV_BUFFER_SIZE] = {0}; /* temp destination buffer */
size_t implied; /* destination implied bits */
size_t mpos; /* offset to useful mant in src */
size_t msize = 0; /* useful size of mantissa in src */
size_t mrsh; /* amount to right shift mantissa */
bool reverse = true; /* if reversed the order of destination */
bool denormalized = false; /* is either source or destination denormalized? */
bool carry = false; /* carry after rounding mantissa */
H5T_conv_float_specval_t specval_type; /* floating-point value type (regular, +/-Inf, +/-0, NaN) */
H5T_conv_ret_t except_ret = H5T_CONV_UNHANDLED; /* return of conversion exception callback function */
ssize_t bitno = 0; /* bit number */
int64_t expo; /* exponent */
uint8_t dbuf[TEMP_FLOAT_CONV_BUFFER_SIZE] = {0}; /* temp destination buffer */
size_t implied; /* destination implied bits */
size_t mpos; /* offset to useful mant in src */
size_t msize = 0; /* useful size of mantissa in src */
size_t mrsh; /* amount to right shift mantissa */
bool reverse = true; /* if reversed the order of destination */
bool denormalized = false; /* is either source or destination denormalized? */
bool carry = false; /* carry after rounding mantissa */

/*
* If the source and destination buffers overlap then use a
Expand Down Expand Up @@ -263,70 +264,21 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
"VAX byte ordering is unsupported for complex number type conversions");
}

/* Find the sign bit value of the source. */
sign = H5T__bit_get_d(s, src_atomic.u.f.sign, (size_t)1);

/* Check for special cases: +0, -0, +Inf, -Inf, NaN */
if (H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize, H5T_BIT_LSB, true) < 0) {
if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, true) < 0) {
/* +0 or -0 */
H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1);
H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, false);
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false);
goto padding;
}
else if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) {
/* +Inf or -Inf */
if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */
H5T_conv_except_t except_type;

/* reverse order first */
if (H5T_FLOAT == src_p->shared->type)
H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order);
else { /* reverse real and imaginary parts of complex number independently */
size_t part_size = src_p->shared->size / 2;
H5T__reverse_order(src_rev, s, part_size, src_atomic.order);
H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order);
}

except_type = sign ? H5T_CONV_EXCEPT_NINF : H5T_CONV_EXCEPT_PINF;
except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id,
conv_ctx->u.conv.dst_type_id, src_rev, d,
conv_ctx->u.conv.cb_struct.user_data);
}

if (except_ret == H5T_CONV_UNHANDLED) {
H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1);
H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true);
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false);
/* If the destination has no implied mantissa bit, we'll need to set
* the 1st bit of mantissa to 1. The Intel-Linux "long double" is
* this case. */
if (H5T_NORM_NONE == dst_atomic.u.f.norm)
H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true);
}
else if (except_ret == H5T_CONV_HANDLED) {
/* No need to reverse the order of destination because user handles it */
reverse = false;
goto next;
}
else if (except_ret == H5T_CONV_ABORT)
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "can't handle conversion exception");

goto padding;
}
specval_type = H5T__conv_float_find_special(s, &src_atomic);
if (specval_type == H5T_CONV_FLOAT_SPECVAL_POSZERO || specval_type == H5T_CONV_FLOAT_SPECVAL_NEGZERO) {
H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1);
H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, false);
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false);
goto padding;
}
else if (H5T_NORM_NONE == src_atomic.u.f.norm &&
H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize - 1, H5T_BIT_LSB, true) < 0 &&
H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) {
/* This is a special case for the source of no implied mantissa bit.
* If the exponent bits are all 1s and only the 1st bit of mantissa
* is set to 1. It's infinity. The Intel-Linux "long double" is this case. */
/* +Inf or -Inf */
if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */
H5T_conv_except_t except_type;
else if (specval_type != H5T_CONV_FLOAT_SPECVAL_REGULAR) {
/* If user's exception handler is present, use it */
if (conv_ctx->u.conv.cb_struct.func) {
H5T_conv_except_t except_type; /* type of conversion exception that occurred */

/* reverse order first */
/* reverse source buffer order first */
/* TODO: make this transparent for complex vs float types */
if (H5T_FLOAT == src_p->shared->type)
H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order);
else { /* reverse real and imaginary parts of complex number independently */
Expand All @@ -335,59 +287,33 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order);
}

except_type = sign ? H5T_CONV_EXCEPT_NINF : H5T_CONV_EXCEPT_PINF;
except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id,
if (specval_type == H5T_CONV_FLOAT_SPECVAL_POSINF)
except_type = H5T_CONV_EXCEPT_PINF;
else if (specval_type == H5T_CONV_FLOAT_SPECVAL_NEGINF)
except_type = H5T_CONV_EXCEPT_NINF;
else
except_type = H5T_CONV_EXCEPT_NAN;

except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id,
conv_ctx->u.conv.dst_type_id, src_rev, d,
conv_ctx->u.conv.cb_struct.user_data);
}

if (except_ret == H5T_CONV_UNHANDLED) {
H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1);
H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true);
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false);
/* If the destination has no implied mantissa bit, we'll need to set
* the 1st bit of mantissa to 1. The Intel-Linux "long double" is
* this case. */
if (H5T_NORM_NONE == dst_atomic.u.f.norm)
H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true);
}
else if (except_ret == H5T_CONV_HANDLED) {
/* No need to reverse the order of destination because user handles it */
reverse = false;
goto next;
}
else if (except_ret == H5T_CONV_ABORT)
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "can't handle conversion exception");

goto padding;
/* Temporary solution to handle VAX special values.
* Note that even though we don't support VAX anymore, we
* still need to handle legacy VAX files so this code must
* remain in place.
*/
}
else if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) {
/* NaN */
if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */
/* reverse order first */
if (H5T_FLOAT == src_p->shared->type)
H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order);
else { /* reverse real and imaginary parts of complex number independently */
size_t part_size = src_p->shared->size / 2;
H5T__reverse_order(src_rev, s, part_size, src_atomic.order);
H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order);
if (specval_type == H5T_CONV_FLOAT_SPECVAL_NAN)
/* There are many NaN values, so we just set all bits of the significand. */
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, true);
else {
/* +/-Inf */
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false);
/* If the destination has no implied mantissa bit, we'll need to set
* the 1st bit of mantissa to 1. The Intel-Linux "long double" is
* this case. */
if (H5T_NORM_NONE == dst_atomic.u.f.norm)
H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true);
}

except_ret = (conv_ctx->u.conv.cb_struct.func)(
H5T_CONV_EXCEPT_NAN, conv_ctx->u.conv.src_type_id, conv_ctx->u.conv.dst_type_id, src_rev,
d, conv_ctx->u.conv.cb_struct.user_data);
}

if (except_ret == H5T_CONV_UNHANDLED) {
/* There are many NaN values, so we just set all bits of the significand. */
H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1);
H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true);
H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, true);
}
else if (except_ret == H5T_CONV_HANDLED) {
/* No need to reverse the order of destination because user handles it */
Expand Down Expand Up @@ -713,6 +639,59 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__conv_f_f_loop() */

/*-------------------------------------------------------------------------
* Function: H5T__conv_float_find_special
*
* Purpose: Helper function to inspect the bits of a floating-point
* value during data conversions and determine if that value
* is a special value (+/-Inf, +/-0, NaN).
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
H5T_conv_float_specval_t
H5T__conv_float_find_special(const uint8_t *src_buf, const H5T_atomic_t *src_atomic)
{
uint64_t sign; /* sign bit value */
H5T_conv_float_specval_t ret_value = H5T_CONV_FLOAT_SPECVAL_REGULAR;

FUNC_ENTER_PACKAGE_NOERR

assert(src_buf);
assert(src_atomic);

/* Find the sign bit value of the source. */
sign = H5T__bit_get_d(src_buf, src_atomic->u.f.sign, (size_t)1);

/* Is the mantissa all 0 bits? */
if (H5T__bit_find(src_buf, src_atomic->u.f.mpos, src_atomic->u.f.msize, H5T_BIT_LSB, true) < 0) {
/* Is the exponent all 0 bits? */
if (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, true) < 0)
/* +0 or -0 */
ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGZERO : H5T_CONV_FLOAT_SPECVAL_POSZERO;
/* Is the exponent all 1 bits? */
else if (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, false) < 0)
/* +Inf or -Inf */
ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGINF : H5T_CONV_FLOAT_SPECVAL_POSINF;
}
else {
bool exp_all_ones = (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, false) < 0);

/* For a source value with no implied mantissa bit, if the exponent bits
* are all 1s and only the 1st bit of the mantissa is set to 1, the value
* is infinity. The Intel-Linux "long double" is this case.
*/
if (H5T_NORM_NONE == src_atomic->u.f.norm && exp_all_ones &&
H5T__bit_find(src_buf, src_atomic->u.f.mpos, src_atomic->u.f.msize - 1, H5T_BIT_LSB, true) < 0)
ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGINF : H5T_CONV_FLOAT_SPECVAL_POSINF;
else if (exp_all_ones)
ret_value = H5T_CONV_FLOAT_SPECVAL_NAN;
}

FUNC_LEAVE_NOAPI(ret_value);
} /* end H5T__conv_float_find_special() */

/*-------------------------------------------------------------------------
* Function: H5T__conv_f_i
*
Expand Down Expand Up @@ -868,8 +847,9 @@ H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer");

/* Allocate space for order-reversed source buffer */
if (NULL == (src_rev = H5MM_calloc(src_p->shared->size)))
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer");
if (conv_ctx->u.conv.cb_struct.func)
if (NULL == (src_rev = H5MM_calloc(src_p->shared->size)))
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer");

/* The conversion loop */
for (size_t elmtno = 0; elmtno < nelmts; elmtno++) {
Expand Down Expand Up @@ -958,6 +938,7 @@ H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t
sign = (hssize_t)H5T__bit_get_d(s, src_atomic.u.f.sign, (size_t)1);

/* Check for special cases: +0, -0, +Inf, -Inf, NaN */
/* TODO: refactor with new function */
if (H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize, H5T_BIT_LSB, true) < 0) {
if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, true) < 0) {
/* +0 or -0 */
Expand Down
15 changes: 15 additions & 0 deletions src/H5Tconv_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@

#define TEMP_FLOAT_CONV_BUFFER_SIZE 64

/****************************/
/* Library Private Typedefs */
/****************************/

typedef enum {
H5T_CONV_FLOAT_SPECVAL_REGULAR,
H5T_CONV_FLOAT_SPECVAL_POSZERO,
H5T_CONV_FLOAT_SPECVAL_NEGZERO,
H5T_CONV_FLOAT_SPECVAL_POSINF,
H5T_CONV_FLOAT_SPECVAL_NEGINF,
H5T_CONV_FLOAT_SPECVAL_NAN,
} H5T_conv_float_specval_t;

/***********************/
/* Function Prototypes */
/***********************/
Expand All @@ -32,6 +45,8 @@ H5_DLL herr_t H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H
H5_DLL herr_t H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t *conv_ctx,
size_t nelmts, size_t buf_stride, void *buf);

H5_DLL H5T_conv_float_specval_t H5T__conv_float_find_special(const uint8_t *src_buf, const H5T_atomic_t *src_atomic);

/****************************************/
/* Soft (emulated) conversion functions */
/****************************************/
Expand Down
Loading

0 comments on commit 88c7fc9

Please sign in to comment.