Merge pull request #2130 from nco/csz_gbg

Granular BitGroom feature for netcdf-c
This commit is contained in:
Ward Fisher 2022-01-13 17:35:16 -07:00 committed by GitHub
commit 3980d7616a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 203 additions and 79 deletions

View File

@ -329,11 +329,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. */

View File

@ -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

View File

@ -467,18 +467,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.
@ -487,7 +488,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
@ -500,10 +501,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
@ -516,7 +517,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
@ -528,7 +529,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

View File

@ -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

View File

@ -1202,13 +1202,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)

View File

@ -721,10 +721,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 &&
@ -806,22 +807,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.
@ -839,7 +842,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).

View File

@ -1022,7 +1022,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);

View File

@ -511,10 +511,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
@ -526,17 +527,27 @@ 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 mnt; /* [frc] Mantissa, 0.5 <= mnt < 1.0 */
double mnt_fabs; /* [frc] fabs(mantissa) */
double mnt_log10_fabs; /* [frc] log10(fabs(mantissa))) */
double val; /* [frc] Copy of input value to avoid indirection */
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 */
@ -559,19 +570,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. */
@ -580,41 +583,68 @@ 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;
/* Bit Set mask for OR: Put ones into bits to be set, zeros in
* untouched bits. */
msk_f64_u64_one =~ msk_f64_u64_zro;
}
/* Parameters BitGroom needs to be set once */
if (quantize_mode == NC_QUANTIZE_BITGROOM)
{
/* 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;
}
}
} /* endif quantize */
/* OK, this is ugly. If you can think of anything better, I'm open
@ -1401,7 +1431,79 @@ 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_FLT - 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_DBL - prc_bnr_xpl_rqr;
msk_f64_u64_zro = 0ull; /* 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;
}

View File

@ -98,10 +98,10 @@ main(int argc, char **argv)
if (nc_def_var_quantize(ncid, NC_GLOBAL, NC_QUANTIZE_BITGROOM, NSD_3) != NC_EGLOBAL) ERR;
if (nc_def_var_quantize(ncid, varid2 + 1, NC_QUANTIZE_BITGROOM, NSD_3) != NC_ENOTVAR) ERR;
/* Invalid values. */
if (nc_def_var_quantize(ncid, varid1, NC_QUANTIZE_BITGROOM + 1, NSD_3) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid1, NC_QUANTIZE_GRANULARBG + 1, NSD_3) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid1, NC_QUANTIZE_BITGROOM, -1) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid1, NC_QUANTIZE_BITGROOM, NC_QUANTIZE_MAX_FLOAT_NSD + 1) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid2, NC_QUANTIZE_BITGROOM + 1, 3) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid2, NC_QUANTIZE_GRANULARBG + 1, 3) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid2, NC_QUANTIZE_BITGROOM, -1) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid2, NC_QUANTIZE_BITGROOM, NC_QUANTIZE_MAX_DOUBLE_NSD + 1) != NC_EINVAL) ERR;
if (nc_def_var_quantize(ncid, varid2, NC_QUANTIZE_BITGROOM, 0) != NC_EINVAL) ERR;
@ -242,9 +242,9 @@ main(int argc, char **argv)
if (quantize_mode_in != NC_QUANTIZE_BITGROOM || nsd_in != NSD_3) ERR;
/* Each var now has an attribute describing the quantize settings. */
if (nc_get_att_int(ncid, 0, NC_QUANTIZE_ATT_NAME, &nsd_att_in)) ERR;
if (nc_get_att_int(ncid, 0, NC_QUANTIZE_BITGROOM_ATT_NAME, &nsd_att_in)) ERR;
if (nsd_att_in != NSD_3) ERR;
if (nc_get_att_int(ncid, 1, NC_QUANTIZE_ATT_NAME, &nsd_att_in)) ERR;
if (nc_get_att_int(ncid, 1, NC_QUANTIZE_BITGROOM_ATT_NAME, &nsd_att_in)) ERR;
if (nsd_att_in != NSD_3) ERR;
/* Check the data. */
@ -447,9 +447,9 @@ main(int argc, char **argv)
if (quantize_mode_in != NC_QUANTIZE_BITGROOM || nsd_in != NSD_3) ERR;
/* Each var now has an attribute describing the quantize settings. */
if (nc_get_att_int(ncid, 0, NC_QUANTIZE_ATT_NAME, &nsd_att_in)) ERR;
if (nc_get_att_int(ncid, 0, NC_QUANTIZE_BITGROOM_ATT_NAME, &nsd_att_in)) ERR;
if (nsd_att_in != NSD_3) ERR;
if (nc_get_att_int(ncid, 1, NC_QUANTIZE_ATT_NAME, &nsd_att_in)) ERR;
if (nc_get_att_int(ncid, 1, NC_QUANTIZE_BITGROOM_ATT_NAME, &nsd_att_in)) ERR;
if (nsd_att_in != NSD_3) ERR;
/* Check the data. */