From fb70b4cf468710180f525ce336c840d3ef74fa4f Mon Sep 17 00:00:00 2001 From: Charlie Zender Date: Wed, 20 Oct 2021 16:00:32 -0700 Subject: [PATCH] First draft of Granular BitGroom feature for netcdf-c --- include/netcdf.h | 10 +- include/netcdf_meta.h.in | 2 +- libdispatch/dvar.c | 27 +++--- libdispatch/dvarinq.c | 6 +- libhdf5/hdf5open.c | 15 ++- libhdf5/hdf5var.c | 19 ++-- libhdf5/nc4hdf.c | 7 +- libsrc4/nc4var.c | 196 +++++++++++++++++++++++++++++++-------- 8 files changed, 211 insertions(+), 71 deletions(-) diff --git a/include/netcdf.h b/include/netcdf.h index 084948af6..48571f203 100644 --- a/include/netcdf.h +++ b/include/netcdf.h @@ -327,11 +327,13 @@ there. */ #define NC_MAX_DEFLATE_LEVEL 9 /**< Maximum deflate level. */ #define NC_NOQUANTIZE 0 /**< No quantization in use. */ -#define NC_QUANTIZE_BITGROOM 1 /**< Use bitgroom quantization. */ +#define NC_QUANTIZE_BITGROOM 1 /**< Use BitGroom quantization. */ +#define NC_QUANTIZE_GRANULARBG 2 /**< Use Granular BitGroom quantization. */ -/** When quantization is used for a variable, an attribute of this - * name is added. */ -#define NC_QUANTIZE_ATT_NAME "_QuantizeBitgroomNumberOfSignificantDigits" +/** When quantization is used for a variable, an attribute of the + * appropriate name is added. */ +#define NC_QUANTIZE_BITGROOM_ATT_NAME "_QuantizeBitgroomNumberOfSignificantDigits" +#define NC_QUANTIZE_GRANULARBG_ATT_NAME "_QuantizeGranularBitGroomNumberOfSignificantDigits" /** For quantization, the allowed value of number of significant * digits for float. */ diff --git a/include/netcdf_meta.h.in b/include/netcdf_meta.h.in index 8c11de950..292d44242 100644 --- a/include/netcdf_meta.h.in +++ b/include/netcdf_meta.h.in @@ -63,6 +63,6 @@ #define NC_HAS_NCZARR @NC_HAS_NCZARR@ /*!< Parallel I/O with filter support. */ #define NC_HAS_MULTIFILTERS @NC_HAS_MULTIFILTERS@ /*!< Nczarr support. */ #define NC_HAS_LOGGING @NC_HAS_LOGGING@ /*!< Logging support. */ -#define NC_HAS_QUANTIZE @NC_HAS_QUANTIZE@ +#define NC_HAS_QUANTIZE @NC_HAS_QUANTIZE@ /*!< Quantization support. */ #endif diff --git a/libdispatch/dvar.c b/libdispatch/dvar.c index 80971cdd8..558abc03d 100644 --- a/libdispatch/dvar.c +++ b/libdispatch/dvar.c @@ -464,18 +464,19 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le /** Turn on quantization for a variable. - The data data are quantized by setting unneeded bits alternately to - 1/0, so that they may compress well. Quantization is lossy (data - are irretrievably altered), and it improves the compression ratio - provided by a subsequent lossless compression filter. Quantization - alone will not reduce the size of the data - lossless compression - like zlib must also be used (see nc_def_var_deflate()). + The data are quantized by setting unneeded bits to zeros or ones + so that they may compress well. BitGroom sets bits alternately to 1/0, + while Granular BitGroom (GBG) sets (more) bits to zeros. + Quantization is lossy (data are irretrievably altered), and it + improves the compression ratio provided by a subsequent lossless + compression filter. Quantization alone will not reduce the data size. + Lossless compression like zlib must also be used (see nc_def_var_deflate()). Producers of large datasets may find that using quantize with compression will result in significant improvent in the final data size. - This data quantization used the bitgroom algorithm. A notable + This data quantization used the BitGroom algorithm. A notable feature of BitGroom is that the data it processes remain in IEEE754 format after quantization. Therefore the BitGroom algorithm does nothing when data are read. @@ -484,7 +485,7 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le NC_DOUBLE. Attempts to set quantization for other variable types return an error (NC_EINVAL). - Variables which use quantize will have added an attribute with name + Variables that use quantize will have added an attribute with name ::NC_QUANTIZE_ATT_NAME, which will contain the number of significant digits. Users should not delete or change this attribute. This is the only record that quantize has been applied @@ -497,10 +498,10 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le Quantization may be applied to scalar variables. - When type conversion takes place during a write, the it occurs + When type conversion takes place during a write, then it occurs before quantization is applied. For example, if nc_put_var_double() - is called on a variable of type NC_FLOAT, which has quantizze - turned on, then the data are first converted from dounle to float, + is called on a variable of type NC_FLOAT, which has quantize + turned on, then the data are first converted from double to float, then quantization is applied to the float values. As with the deflate settings, quantize settings may only be @@ -513,7 +514,7 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le variable which has been quantized is readable to older versions of the netCDF libraries, and to netCDF-Java. - For more information about quantization and the bitgroom filter, see + For more information about quantization and the BitGroom filter, see Zender, C. S. (2016), Bit Grooming: Statistically accurate precision-preserving quantization with compression, evaluated in @@ -525,7 +526,7 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le @param ncid File ID. @param varid Variable ID. ::NC_GLOBAL may not be used. @param quantize_mode Quantization mode. May be ::NC_NOQUANTIZE or - ::NC_QUANTIZE_BITGROOM. + ::NC_QUANTIZE_BITGROOM or ::NC_QUANTIZE_GRANULARBG. @param nsd Number of significant digits. May be any integer from 1 to ::NC_QUANTIZE_MAX_FLOAT_NSD (for variables of type ::NC_FLOAT) or ::NC_QUANTIZE_MAX_DOUBLE_NSD (for variables of type diff --git a/libdispatch/dvarinq.c b/libdispatch/dvarinq.c index 846bab68d..0af136ad1 100644 --- a/libdispatch/dvarinq.c +++ b/libdispatch/dvarinq.c @@ -528,15 +528,15 @@ nc_inq_var_fill(int ncid, int varid, int *no_fill, void *fill_valuep) } /** @ingroup variables - * Learn whether BitGroom quantization is on for a variable, and, if so, + * Learn whether quantization is on for a variable, and, if so, * the NSD setting. * * @param ncid File ID. * @param varid Variable ID. Must not be NC_GLOBAL. - * @param quantize_modep Pointer that gets a 0 if BitGroom is not in + * @param quantize_modep Pointer that gets a 0 if quantization is not in * use for this var, and a 1 if it is. Ignored if NULL. * @param nsdp Pointer that gets the NSD setting (from 1 to 15), if - * BitGroom is in use. Ignored if NULL. + * quantization is in use. Ignored if NULL. * * @return 0 for success, error code otherwise. * @author Charlie Zender, Ed Hartnett diff --git a/libhdf5/hdf5open.c b/libhdf5/hdf5open.c index ccd484405..78c2ed077 100644 --- a/libhdf5/hdf5open.c +++ b/libhdf5/hdf5open.c @@ -1186,13 +1186,24 @@ static int get_quantize_info(NC_VAR_INFO_T *var) /* Try to open an attribute of the correct name for quantize * info. */ datasetid = ((NC_HDF5_VAR_INFO_T *)var->format_var_info)->hdf_datasetid; - attid = H5Aopen_by_name(datasetid, ".", NC_QUANTIZE_ATT_NAME, + attid = H5Aopen_by_name(datasetid, ".", NC_QUANTIZE_BITGROOM_ATT_NAME, H5P_DEFAULT, H5P_DEFAULT); + if (attid > 0) + { + var->quantize_mode = NC_QUANTIZE_BITGROOM; + } + else + { + attid = H5Aopen_by_name(datasetid, ".", NC_QUANTIZE_GRANULARBG_ATT_NAME, + H5P_DEFAULT, H5P_DEFAULT); + if (attid > 0) + var->quantize_mode = NC_QUANTIZE_GRANULARBG; + } + /* If there is an attribute, read it for the nsd. */ if (attid > 0) { - var->quantize_mode = NC_QUANTIZE_BITGROOM; if (H5Aread(attid, H5T_NATIVE_INT, &var->nsd) < 0) return NC_EHDFERR; if (H5Aclose(attid) < 0) diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index 789ad6e34..a79b13bdc 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -715,10 +715,11 @@ nc_def_var_extra(int ncid, int varid, int *shuffle, int *unused1, { /* Only two valid mode settings. */ if (*quantize_mode != NC_NOQUANTIZE && - *quantize_mode != NC_QUANTIZE_BITGROOM) + *quantize_mode != NC_QUANTIZE_BITGROOM && + *quantize_mode != NC_QUANTIZE_GRANULARBG) return NC_EINVAL; - if (*quantize_mode == NC_QUANTIZE_BITGROOM) + if (*quantize_mode == NC_QUANTIZE_BITGROOM || *quantize_mode == NC_QUANTIZE_GRANULARBG) { /* Only float and double types can have quantization. */ if (var->type_info->hdr.id != NC_FLOAT && @@ -800,22 +801,24 @@ done: * error.) * * When quantize is turned on, and the number of significant digits - * has been specified, then the netCDF library will apply all zeros or + * has been specified, then the netCDF library will quantize according + * to the selected algorithm. BitGroom will apply all zeros or * all ones (alternating) to bits which are not needed to specify the - * value to the number of significant digits. This will change the - * value of the data, but will make it more compressable. + * value to the number of significant digits. GranularBG will zero + * more bits than BG, and thus be more compressible and less accurate. + * Both will change the value of the data, and will make it more compressible. * * Quantizing the data does not reduce the size of the data on disk, * but combining quantize with compression will allow for better * compression. Since the data values are changed, the use of quantize - * and compression such as deflate constitute lossy compression. + * and compression such as DEFLATE constitute lossy compression. * * Producers of large datasets may find that using quantize with * compression will result in significant improvent in the final data * size. * * Variables which use quantize will have added an attribute with name - * ::NC_QUANTIZE_ATT_NAME, which will contain the number of + * ::NC_QUANTIZE_[ALG_NAME]_ATT_NAME, which will contain the number of * significant digits. Users should not delete or change this * attribute. This is the only record that quantize has been applied * to the data. @@ -833,7 +836,7 @@ done: * @param ncid File ID. * @param varid Variable ID. NC_GLOBAL may not be used. * @param quantize_mode Quantization mode. May be ::NC_NOQUANTIZE or - * ::NC_QUANTIZE_BITGROOM. + * ::NC_QUANTIZE_BITGROOM or ::NC_QUANTIZE_GRANULARBG. * @param nsd Number of significant digits. May be any integer from 1 * to ::NC_QUANTIZE_MAX_FLOAT_NSD (for variables of type ::NC_FLOAT) or * ::NC_QUANTIZE_MAX_DOUBLE_NSD (for variables of type ::NC_DOUBLE). diff --git a/libhdf5/nc4hdf.c b/libhdf5/nc4hdf.c index cd0de7e09..b8581cfae 100644 --- a/libhdf5/nc4hdf.c +++ b/libhdf5/nc4hdf.c @@ -1021,7 +1021,12 @@ var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid /* If quantization is in use, write an attribute indicating it, a * single integer which is the number of significant digits. */ if (var->quantize_mode == NC_QUANTIZE_BITGROOM) - if ((retval = nc4_put_att(var->container, var->hdr.id, NC_QUANTIZE_ATT_NAME, NC_INT, 1, + if ((retval = nc4_put_att(var->container, var->hdr.id, NC_QUANTIZE_BITGROOM_ATT_NAME, NC_INT, 1, + &var->nsd, NC_INT, 0))) + BAIL(retval); + + if (var->quantize_mode == NC_QUANTIZE_GRANULARBG) + if ((retval = nc4_put_att(var->container, var->hdr.id, NC_QUANTIZE_GRANULARBG_ATT_NAME, NC_INT, 1, &var->nsd, NC_INT, 0))) BAIL(retval); diff --git a/libsrc4/nc4var.c b/libsrc4/nc4var.c index 69d9fa0c2..ff02a3dde 100644 --- a/libsrc4/nc4var.c +++ b/libsrc4/nc4var.c @@ -499,10 +499,11 @@ NC4_var_par_access(int ncid, int varid, int par_access) * @param fill_value The fill value. * @param strict_nc3 Non-zero if strict model in effect. * @param quantize_mode May be ::NC_NOQUANTIZE or - * ::NC_QUANTIZE_BITGROOM. + * ::NC_QUANTIZE_BITGROOM or ::NC_QUANTIZE_GRANULARBG. * @param nsd Number of significant diggits for quantizize. Ignored - * unless quantize_mode is ::NC_QUANTIZE_BITGROOM. - * + * unless quantize_mode is ::NC_QUANTIZE_BITGROOM or + * ::NC_QUANTIZE_GRANULARBG. + * * @returns ::NC_NOERR No error. * @returns ::NC_EBADTYPE Type not found. * @author Ed Hartnett, Dennis Heimbigner @@ -514,17 +515,23 @@ nc4_convert_type(const void *src, void *dest, const nc_type src_type, int nsd) { /* These vars are used with quantize feature. */ - const double bit_per_dcm_dgt_prc = M_LN10 / M_LN2; /* 3.32 [frc] Bits per decimal digit of precision */ + const double bit_per_dgt = M_LN10 / M_LN2; /* 3.32 [frc] Bits per decimal digit of precision = log2(10) */ + const double dgt_per_bit= M_LN2 / M_LN10; /* 0.301 [frc] Decimal digits per bit of precision = log10(2) */ double mss_val_cmp_dbl; /* Missing value for comparison to double precision values */ float mss_val_cmp_flt; /* Missing value for comparison to single precision values */ int bit_xpl_nbr_zro; /* [nbr] Number of explicit bits to zero */ + int dgt_nbr; /* [nbr] Number of digits before decimal point */ + int qnt_pwr; /* [nbr] Power of two in quantization mask: qnt_msk = 2^qnt_pwr */ + int xpn_bs2; /* [nbr] Binary exponent xpn_bs2 in val = sign(val) * 2^xpn_bs2 * mnt, 0.5 < mnt <= 1.0 */ size_t idx; unsigned int *u32_ptr; unsigned int msk_f32_u32_zro; unsigned int msk_f32_u32_one; + unsigned int msk_f32_u32_hshv; unsigned long long int *u64_ptr; unsigned long long int msk_f64_u64_zro; unsigned long long int msk_f64_u64_one; + unsigned long long int msk_f64_u64_hshv; unsigned short prc_bnr_xpl_rqr; /* [nbr] Explicitly represented binary digits required to retain */ ptr_unn op1; /* I/O [frc] Values to quantize */ @@ -547,19 +554,11 @@ nc4_convert_type(const void *src, void *dest, const nc_type src_type, /* If quantize is in use, set up some values. Quantize can only be * used when the destination type is NC_FLOAT or NC_DOUBLE. */ - if (quantize_mode == NC_QUANTIZE_BITGROOM) + if (quantize_mode != NC_NOQUANTIZE) { assert(dest_type == NC_FLOAT || dest_type == NC_DOUBLE); - /* How many bits to preserve? Being conservative, we round up the - * exact binary digits of precision. Add one because the first bit - * is implicit not explicit but corner cases prevent our taking - * advantage of this. */ - prc_bnr_xpl_rqr = (unsigned short)ceil(nsd * bit_per_dcm_dgt_prc) + 1; - if (dest_type == NC_DOUBLE) - prc_bnr_xpl_rqr++; /* Seems necessary for double-precision - * ppc=array(1.234567,1.0e-6,$dmn) */ - /* Determine masks, copy the data, do the quantization. */ + /* Parameters shared by both BitGroom and GranularBG */ if (dest_type == NC_FLOAT) { /* Determine the fill value. */ @@ -568,40 +567,86 @@ nc4_convert_type(const void *src, void *dest, const nc_type src_type, else mss_val_cmp_flt = NC_FILL_FLOAT; - bit_xpl_nbr_zro = BIT_XPL_NBR_SGN_FLT - prc_bnr_xpl_rqr; - - /* Create mask */ - msk_f32_u32_zro = 0u; /* Zero all bits */ - msk_f32_u32_zro = ~msk_f32_u32_zro; /* Turn all bits to ones */ - - /* Bit Shave mask for AND: Left shift zeros into bits to be - * rounded, leave ones in untouched bits. */ - msk_f32_u32_zro <<= bit_xpl_nbr_zro; - - /* Bit Set mask for OR: Put ones into bits to be set, zeros in - * untouched bits. */ - msk_f32_u32_one = ~msk_f32_u32_zro; } else { - bit_xpl_nbr_zro = BIT_XPL_NBR_SGN_DBL - prc_bnr_xpl_rqr; - assert(bit_xpl_nbr_zro <= BIT_XPL_NBR_SGN_DBL - NCO_PPC_BIT_XPL_NBR_MIN); + + /* Determine the fill value. */ if (fill_value) mss_val_cmp_dbl = *(double *)fill_value; else mss_val_cmp_dbl = NC_FILL_DOUBLE; - /* Create mask. */ - msk_f64_u64_zro = 0ul; /* Zero all bits. */ - msk_f64_u64_zro = ~msk_f64_u64_zro; /* Turn all bits to ones. */ + } - /* Bit Shave mask for AND: Left shift zeros into bits to be - * rounded, leave ones in untouched bits. */ - msk_f64_u64_zro <<= bit_xpl_nbr_zro; + /* Parameters used only by BitGroom */ + if (quantize_mode == NC_QUANTIZE_BITGROOM) + { - /* Bit Set mask for OR: Put ones into bits to be set, zeros in - * untouched bits. */ - msk_f64_u64_one =~ msk_f64_u64_zro; + /* How many bits to preserve? Being conservative, we round up the + * exact binary digits of precision. Add one because the first bit + * is implicit not explicit but corner cases prevent our taking + * advantage of this. */ + prc_bnr_xpl_rqr = (unsigned short)ceil(nsd * bit_per_dgt) + 1; + if (dest_type == NC_DOUBLE) + prc_bnr_xpl_rqr++; /* Seems necessary for double-precision + * ppc=array(1.234567,1.0e-6,$dmn) */ + + if (dest_type == NC_FLOAT) + { + + bit_xpl_nbr_zro = BIT_XPL_NBR_SGN_FLT - prc_bnr_xpl_rqr; + + /* Create mask */ + msk_f32_u32_zro = 0u; /* Zero all bits */ + msk_f32_u32_zro = ~msk_f32_u32_zro; /* Turn all bits to ones */ + + /* Bit Shave mask for AND: Left shift zeros into bits to be + * rounded, leave ones in untouched bits. */ + msk_f32_u32_zro <<= bit_xpl_nbr_zro; + + /* Bit Set mask for OR: Put ones into bits to be set, zeros in + * untouched bits. */ + msk_f32_u32_one = ~msk_f32_u32_zro; + } + else + { + + bit_xpl_nbr_zro = BIT_XPL_NBR_SGN_DBL - prc_bnr_xpl_rqr; + /* Create mask. */ + msk_f64_u64_zro = 0ul; /* Zero all bits. */ + msk_f64_u64_zro = ~msk_f64_u64_zro; /* Turn all bits to ones. */ + + /* Bit Shave mask for AND: Left shift zeros into bits to be + * rounded, leave ones in untouched bits. */ + msk_f64_u64_zro <<= bit_xpl_nbr_zro; + + /* Bit Set mask for OR: Put ones into bits to be set, zeros in + * untouched bits. */ + msk_f64_u64_one =~ msk_f64_u64_zro; + + } + + } + /* Set-up parameters used only by GranularBG */ + else if (quantize_mode == NC_QUANTIZE_GRANULARBG) + { + + if (dest_type == NC_FLOAT) + { + + bit_xpl_nbr_sgn=BIT_XPL_NBR_SGN_FLT; + + } + else + { + + bit_xpl_nbr_sgn=BIT_XPL_NBR_SGN_DBL; + + } + + } + } } /* endif quantize */ @@ -1389,7 +1434,80 @@ nc4_convert_type(const void *src, void *dest, const nc_type src_type, if (op1.dp[idx] != mss_val_cmp_dbl && u64_ptr[idx] != 0ULL) /* Never quantize upwards floating point values of zero */ u64_ptr[idx] |= msk_f64_u64_one; } - } /* endif quantize */ + } /* endif BitGroom */ + + + if (quantize_mode == NC_QUANTIZE_GRANULARBG) + { + if (dest_type == NC_FLOAT) + { + /* Granular BitGroom */ + op1.fp = (float *)dest; + u32_ptr = op1.ui32p; + for (idx = 0L; idx < len; idx++) + { + + if((val=op1.fp[idx]) != mss_val_cmp_flt && u32_ptr[idx] != 0U) + { + mnt=frexp(val,&xpn_bs2); /* DGG19 p. 4102 (8) */ + mnt_fabs=fabs(mnt); + mnt_log10_fabs=log10(mnt_fabs); + /* 20211003 Continuous determination of dgt_nbr improves CR by ~10% */ + dgt_nbr=(int)floor(xpn_bs2*dgt_per_bit+mnt_log10_fabs)+1; /* DGG19 p. 4102 (8.67) */ + qnt_pwr=(int)floor(bit_per_dgt*(dgt_nbr-nsd)); /* DGG19 p. 4101 (7) */ + prc_bnr_xpl_rqr= mnt_fabs == 0.0 ? 0 : abs((int)floor(xpn_bs2-bit_per_dgt*mnt_log10_fabs)-qnt_pwr); /* Protect against mnt = -0.0 */ + prc_bnr_xpl_rqr--; /* 20211003 Reduce formula result by 1 bit: Passes all tests, improves CR by ~10% */ + + bit_xpl_nbr_zro=bit_xpl_nbr_sgn-prc_bnr_xpl_rqr; + msk_f32_u32_zro=0u; /* Zero all bits */ + msk_f32_u32_zro=~msk_f32_u32_zro; /* Turn all bits to ones */ + /* Bit Shave mask for AND: Left shift zeros into bits to be rounded, leave ones in untouched bits */ + msk_f32_u32_zro <<= bit_xpl_nbr_zro; + /* Bit Set mask for OR: Put ones into bits to be set, zeros in untouched bits */ + msk_f32_u32_one=~msk_f32_u32_zro; + msk_f32_u32_hshv=msk_f32_u32_one & (msk_f32_u32_zro >> 1); /* Set one bit: the MSB of LSBs */ + u32_ptr[idx]+=msk_f32_u32_hshv; /* Add 1 to the MSB of LSBs, carry 1 to mantissa or even exponent */ + u32_ptr[idx]&=msk_f32_u32_zro; /* Shave it */ + + } /* !mss_val_cmp_flt */ + + } + } + else + { + /* Granular BitGroom */ + op1.dp = (double *)dest; + u64_ptr = op1.ui64p; + for (idx = 0L; idx < len; idx++) + { + + if((val=op1.dp[idx]) != mss_val_cmp_dbl && u64_ptr[idx] != 0ULL) + { + mnt=frexp(val,&xpn_bs2); /* DGG19 p. 4102 (8) */ + mnt_fabs=fabs(mnt); + mnt_log10_fabs=log10(mnt_fabs); + /* 20211003 Continuous determination of dgt_nbr improves CR by ~10% */ + dgt_nbr=(int)floor(xpn_bs2*dgt_per_bit+mnt_log10_fabs)+1; /* DGG19 p. 4102 (8.67) */ + qnt_pwr=(int)floor(bit_per_dgt*(dgt_nbr-nsd)); /* DGG19 p. 4101 (7) */ + prc_bnr_xpl_rqr= mnt_fabs == 0.0 ? 0 : abs((int)floor(xpn_bs2-bit_per_dgt*mnt_log10_fabs)-qnt_pwr); /* Protect against mnt = -0.0 */ + prc_bnr_xpl_rqr--; /* 20211003 Reduce formula result by 1 bit: Passes all tests, improves CR by ~10% */ + + bit_xpl_nbr_zro=bit_xpl_nbr_sgn-prc_bnr_xpl_rqr; + msk_f64_u64_zro=0u; /* Zero all bits */ + msk_f64_u64_zro=~msk_f64_u64_zro; /* Turn all bits to ones */ + /* Bit Shave mask for AND: Left shift zeros into bits to be rounded, leave ones in untouched bits */ + msk_f64_u64_zro <<= bit_xpl_nbr_zro; + /* Bit Set mask for OR: Put ones into bits to be set, zeros in untouched bits */ + msk_f64_u64_one=~msk_f64_u64_zro; + msk_f64_u64_hshv=msk_f64_u64_one & (msk_f64_u64_zro >> 1); /* Set one bit: the MSB of LSBs */ + u64_ptr[idx]+=msk_f64_u64_hshv; /* Add 1 to the MSB of LSBs, carry 1 to mantissa or even exponent */ + u64_ptr[idx]&=msk_f64_u64_zro; /* Shave it */ + + } /* !mss_val_cmp_dbl */ + + } + } + } /* endif GranularBG */ return NC_NOERR; }