Skip to content

Commit

Permalink
Better precision support in bdldfp formatting. (#5034)
Browse files Browse the repository at this point in the history
  • Loading branch information
cppguru authored and GitHub Enterprise committed Oct 21, 2024
1 parent 9ee9dc0 commit edd0fa6
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 152 deletions.
7 changes: 5 additions & 2 deletions groups/bdl/bdldfp/bdldfp_decimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,10 +604,13 @@ DecimalNumPut<CHARTYPE, OUTPUTITERATOR>::do_put_impl(
if (format.flags() & bsl::ios::fixed) {
cfg.setStyle(DecimalFormatConfig::e_FIXED);
}

if (format.flags() & bsl::ios::scientific) {
else if (format.flags() & bsl::ios::scientific) {
cfg.setStyle(DecimalFormatConfig::e_SCIENTIFIC);
}
else {
// Disable precision for natural format
cfg.setPrecision(-1);
}

if (format.flags() & bsl::ios::showpos) {
cfg.setSign(DecimalFormatConfig::e_ALWAYS);
Expand Down
44 changes: 22 additions & 22 deletions groups/bdl/bdldfp/bdldfp_decimalformatconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ BSLS_IDENT("$Id$")
// Name Type Default Simple Constraints
// --------- ------ --------------- ------------------
// style Style e_NATURAL none
// precision int 0 >= 0
// precision int 0 >= -1
// sign Sign e_NEGATIVE_ONLY none
// infinity string "inf" none
// nan string "nan" none
Expand All @@ -43,11 +43,12 @@ BSLS_IDENT("$Id$")
// `e_NATURAL`, the number is written according to the description of
// `to-scientific-string` found in
// http://speleotrove.com/decimal/decarith.pdf (and no other specified
// formatting values are used, including precision).
// * `precision`: control how many digits are written after the decimal point
// if the decimal number is rendered in `e_FIXED` and `e_SCIENTIFIC`
// formats. Note that `precision` attribute is ignored in `e_NATURAL`
// format.
// formatting values are used, except precision that has to be set to -1 to
// get the actual natural format with the natural precision).
// * `precision`: control how many digits are written after the decimal point.
// When -1 is specified for precision, the number is written using its
// natural precision in every format, while specifying a zero or positive
// precision overrides said natural precision.
// * `sign`: control how the sign is output. If a decimal value has its sign
// bit set, a `-` is always written. Otherwise, if `sign` is
// `e_NEGATIVE_ONLY`, no sign is written. If it is `e_ALWAYS`, a `+` is
Expand Down Expand Up @@ -134,18 +135,17 @@ class DecimalFormatConfig {

/// Create an object of this class having the specified `precision` to
/// control how many digits are written after a decimal point. The
/// behavior is undefined if `precision` is negative. Optionally
/// specify `style` to control how the number is written. If it is not
/// specified, `e_NATURAL` is used. Optionally specify `sign` to
/// control how the sign is output. If is not specified,
/// `e_NEGATIVE_ONLY` is used. Optionally specify `infinity` as a
/// string to output infinity value. If it is not specified, "inf" is
/// used. Optionally specify `nan` as a string to output NaN value. If
/// it is not specified, "nan" is used. Optionally specify `snan` as a
/// string to output signaling NaN value. If it is not specified,
/// "snan" is used. The behavior is undefined unless the pointers to
/// `infinity`, `nan` and `snan` remain valid for the lifetime of this
/// object. Optionally specify `point` as the character to use for
/// behavior is undefined unless `precision >= -1`. Optionally specify
/// `style` to control how the number is written. If it is not specified,
/// `e_NATURAL` is used. Optionally specify `sign` to control how the sign
/// is output. If is not specified, `e_NEGATIVE_ONLY` is used. Optionally
/// specify `infinity` as a string to output infinity value. If it is not
/// specified, "inf" is used. Optionally specify `nan` as a string to
/// output NaN value. If it is not specified, "nan" is used. Optionally
/// specify `snan` as a string to output signaling NaN value. If it is not
/// specified, "snan" is used. The behavior is undefined unless the
/// pointers to `infinity`, `nan` and `snan` remain valid for the lifetime
/// of this object. Optionally specify `point` as the character to use for
/// decimal points. If it is not specified, `.` is used. Optionally
/// specify `exponent` as the character to use for exponent. If it is
/// not specified, `e` is used. Optionally specify `showpoint` to force
Expand All @@ -171,7 +171,7 @@ class DecimalFormatConfig {
// MANIPULATORS

/// Set the `precision` attribute of this object to the specified
/// `value`. Behavior is undefined if `value` is negative.
/// `value`. Behavior is undefined unless `value >= -1`.
void setPrecision (int value);

/// Set the `style` attribute of this object to the specified `value`.
Expand Down Expand Up @@ -308,7 +308,7 @@ DecimalFormatConfig::DecimalFormatConfig(int precision,
, d_showpoint(showpoint)
, d_expWidth(expWidth)
{
BSLS_ASSERT(precision >= 0);
BSLS_ASSERT(precision >= -1);
BSLS_ASSERT(infinity);
BSLS_ASSERT(nan);
BSLS_ASSERT(snan);
Expand All @@ -318,9 +318,9 @@ DecimalFormatConfig::DecimalFormatConfig(int precision,

// MANIPULATORS
inline
void DecimalFormatConfig::setPrecision (int value)
void DecimalFormatConfig::setPrecision(int value)
{
BSLS_ASSERT(value >= 0);
BSLS_ASSERT(value >= -1);
d_precision = value;
}

Expand Down
6 changes: 4 additions & 2 deletions groups/bdl/bdldfp/bdldfp_decimalformatconfig.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1791,8 +1791,9 @@ int main(int argc, char* argv[])

if (veryVerbose) cout << "\tPrecision\n";
{
ASSERT_FAIL(obj.setPrecision(-1));
ASSERT_FAIL(obj.setPrecision(-2));
ASSERT_PASS(obj.setPrecision(0));
ASSERT_PASS(obj.setPrecision(-1)); // the natural precision
}

if (veryVerbose) cout << "\tInfinity\n";
Expand Down Expand Up @@ -1827,7 +1828,8 @@ int main(int argc, char* argv[])
if (veryVerbose) cout << "\tConstructor\n";
{
ASSERT_PASS(Obj(D1, D2, D3, D4, D5, D6, D7, D8, D9, D10));
ASSERT_FAIL(Obj(-1, D2, D3, D4, D5, D6, D7, D8, D9, D10));
ASSERT_PASS(Obj(-1, D2, D3, D4, D5, D6, D7, D8, D9, D10));
ASSERT_FAIL(Obj(-2, D2, D3, D4, D5, D6, D7, D8, D9, D10));
ASSERT_FAIL(Obj(D1, D2, D3, 0, D5, D6, D7, D8, D9, D10));
ASSERT_FAIL(Obj(D1, D2, D3, D4, 0, D6, D7, D8, D9, D10));
ASSERT_FAIL(Obj(D1, D2, D3, D4, D5, 0, D7, D8, D9, D10));
Expand Down
108 changes: 95 additions & 13 deletions groups/bdl/bdldfp/bdldfp_decimalimputil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ int formatScientific(char *buffer,
/// unspecified state, with the returned value indicating the necessary
/// size. The behavior is undefined if the `length` is negative and
/// `buffer` is not null. The `buffer` is permitted to be null if the
/// `length` is not positive. This can be used to determine the necessary
/// `length` is not positive. This can be used to determine the necessary
/// buffer size.
template <class DECIMAL>
int formatNatural(char *buffer,
Expand Down Expand Up @@ -382,14 +382,83 @@ int formatNatural(char *buffer,
DecimalFormatConfig config(cfg);

if (e <= 0 && adjustedExponent >= -6) {
config.setPrecision(-e);
if (-1 == config.precision()) {
config.setPrecision(-e);
}
return formatFixed(buffer, length, value, config); // RETURN
}

if (-1 == config.precision()) {
config.setPrecision(maxDigit - 1);
}
return formatScientific(buffer, length, value, config);
}

/// Convert the specified decimal `value` to character string using
/// `e_SCIENTIFIC` style and the specified `cfg` formatting options but
/// using the natural precision of `value`. Load the result into the
/// specified `buffer`. If the length of resultant string exceeds the
/// specified `length`, then the `buffer` will be left in an unspecified
/// state, with the returned value indicating the necessary size. The
/// behavior is undefined if the `length` is negative and `buffer` is not
/// null. The `buffer` is permitted to be null if the `length` is not
/// positive. This can be used to determine the necessary buffer size.
template <class DECIMAL>
int formatScientificWithNaturalPrecision(char *buffer,
int length,
DECIMAL value,
const DecimalFormatConfig& cfg)
{
BSLS_ASSERT(buffer || length < 0);

typedef typename DecimalTraits<DECIMAL>::Significand SIGNIFICAND;

int sign;
int e;
SIGNIFICAND s;
int cls = DecimalImpUtil::decompose(&sign, &s, &e, value);
(void)cls;
BSLS_ASSERT(cls == FP_ZERO || cls == FP_NORMAL || cls == FP_SUBNORMAL);

int maxDigit = getMostSignificandPlace(s);

DecimalFormatConfig config(cfg);

config.setPrecision(maxDigit - 1);
return formatScientific(buffer, length, value, config);
}

/// Convert the specified decimal `value` to character string using
/// `e_FIXED` style and the specified `cfg` formatting options but using
/// the natural precision of `value`. Load the result into the specified
/// `buffer`. If the length of resultant string exceeds the specified
/// `length`, then the `buffer` will be left in an unspecified state, with
/// the returned value indicating the necessary size. The behavior is
/// undefined if the `length` is negative and `buffer` is not null. The
/// `buffer` is permitted to be null if the `length` is not positive. This
/// can be used to determine the necessary buffer size.
template <class DECIMAL>
int formatFixedWithNaturalPrecision(char *buffer,
int length,
DECIMAL value,
const DecimalFormatConfig& cfg)
{
BSLS_ASSERT(buffer || length < 0);

typedef typename DecimalTraits<DECIMAL>::Significand SIGNIFICAND;

int sign;
int e;
SIGNIFICAND s;
int cls = DecimalImpUtil::decompose(&sign, &s, &e, value);
(void)cls;
BSLS_ASSERT(cls == FP_ZERO || cls == FP_NORMAL || cls == FP_SUBNORMAL);

DecimalFormatConfig config(cfg);
config.setPrecision(-e);
return formatFixed(buffer, length, value, config); // RETURN
}

int formatSpecial(char *buffer,
int length,
const char *begin,
Expand All @@ -414,7 +483,7 @@ int formatImpl(char *buffer,
const DecimalFormatConfig& cfg)
{
BSLS_ASSERT(buffer || length < 0);
BSLS_ASSERT(cfg.precision() >= 0);
BSLS_ASSERT(cfg.precision() >= -1);

typedef typename DecimalTraits<DECIMAL>::Significand SIGNIFICAND;

Expand All @@ -439,29 +508,42 @@ int formatImpl(char *buffer,
char *it = buffer + signLength;
int decimalLength = 0;

const bool naturalPrecision = -1 == cfg.precision();

switch (cls) {
case FP_NORMAL:
case FP_SUBNORMAL:
case FP_ZERO: {
if (DecimalFormatConfig::e_NATURAL != cfg.style()) {
if (!naturalPrecision) {
value = DecimalImpUtil::normalize(value);
}

switch (cfg.style()) {
case DecimalFormatConfig::e_SCIENTIFIC: {

decimalLength = formatScientific(it, bufferLength, value, cfg);

if (naturalPrecision) {
decimalLength = formatScientificWithNaturalPrecision(
it,
bufferLength,
value,
cfg);
}
else {
decimalLength = formatScientific(it, bufferLength, value, cfg);
}
} break;
case DecimalFormatConfig::e_FIXED: {

decimalLength = formatFixed(it, bufferLength, value, cfg);

if (naturalPrecision) {
decimalLength = formatFixedWithNaturalPrecision(it,
bufferLength,
value,
cfg);
}
else {
decimalLength = formatFixed(it, bufferLength, value, cfg);
}
} break;
case DecimalFormatConfig::e_NATURAL: {

decimalLength = formatNatural(it, bufferLength, value, cfg);

decimalLength = formatNatural(it, bufferLength, value, cfg);
} break;
default: {
BSLS_ASSERT(0 == "Unexpected format style value");
Expand Down
Loading

0 comments on commit edd0fa6

Please sign in to comment.