6

The macro constant DECIMAL_DIG is the

number of decimal digits that can be converted to long double and back without losing precision.

The macro constant LDBL_DIG is the

number of decimal digits that can be represented without losing precision for long double.

What is the difference between these two definitions? Is there a case where using one over the other could lead to incorrect results?

On my machine, DECIMAL_DIG == 21, while LDBL_DIG == 18.

Source: 1

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Andrew McKinlay
  • 431
  • 3
  • 15

3 Answers3

8

[Edit Oct 2021]
Next versions of C (C23) may "make DECIMAL_DIG obsolescent".
I recommend you consider alternatives.


What is the difference between DECIMAL_DIG and LDBL_DIG (?)

DECIMAL_DIG concerns widest floating point type to decimal text to widest floating point type conversions.
LDBL_DIG concerns decimal text to long double to decimal text conversions.


First: Narrow the problem

DECIMAL_DIG (available since C99) applies to the widest floating point type. With C11, 3 type specific macros FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG mean the same thing except they apply to the corresponding type, rather than the widest one.

To simplify the problem, let us compare LDBL_DECIMAL_DIG to LDBL_DIG as they both deal with the same type: long double.


decimal text representation --> long double --> decimal text representation.
LDBL_DIG is the maximum significant digits of text that in this round-trip always result in the same starting value.

long double --> decimal text representation --> long double.
LDBL_DECIMAL_DIG is the number of significant digits of text needed in this round-trip to always result in the same starting long double value.

If the floating point type used a base 10 presentation, LDBL_DIG and LDBL_DECIMAL_DIG would have the same value. Yet most C implementations use a binary base 2 instead of 10: FLT_RADIX == 2.


The follows avoids a deep mathematical technical explanation.

long double can not represent every possible value that decimal text representation does. The latter can be s = "0.1234567890123456789012345678901234567890" and common long double can not represent that exactly. Converting s into long double and back to text is not expected to return the same result.

char *s = "0.1234567890123456789012345678901234567890";
long double ld = strtold(s, (char **)NULL);
printf("%.40Le\n", ld);
// typical output        v -- different
// 1.2345678901234567890132180073559098332225e-01

If we limit text input to LDBL_DIG significant digits though, code will always succeed for all values of long double - round trip successfully.

s = "0.123456789012345678";
ld = strtold(s, (char **)NULL);
printf("%d\n%.*Le\n", LDBL_DIG, LDBL_DIG - 1, ld);
// 18
// 1.23456789012345678e-01

This post Printf width specifier to maintain precision of floating-point value details the use of xxx_DECIMAL_DIG family of macros. It shows the number of significant digits need to print a floating-point value to text and then convert back to a FP value and always get the same result.


Note: xxx_DECIMAL_DIG >= xxx_DIG.

LDBL_DIG - 1 used above rather than LDBL_DIG as %.*Le prints a leading digit and then the specified precision number of digits. The total significant digit count should be LDBL_DIG.



Further info to answer Are the definitions I quoted wrong or not?

First definition is close, yet not complete.
LDBL_DIG refers to text --> long double --> text needs.

LDBL_DIG

OP's: "number of decimal digits that can be represented without losing precision for long double."

C Spec: "number of decimal digits, q, such that any floating-point number with q decimal digits can be rounded into a floating-point number with p radix b digits and back again without change to the q decimal digits,"

q = floor((p-1)*log10b)

With OP's machine, long double has p == 64 and b == 2 --> q == 18

Thus a decimal number with up to 18 significant digits, as text, can be converted to a long double and then back to an 18 digit number, in text and always get the starting text value - for the normal long double range.


DECIMAL_DIG

Second definition is amiss.
DECIMAL_DIG refers to long double --> text --> long double needs.
OP's definition speaks of text to long double to text.

OP's: "number of decimal digits that can be converted to long double and back without losing precision."

C Spec: "number of decimal digits, n, such that any floating-point number in the widest supported floating type with pmax radix b digits can be rounded to a floating-point number with n decimal digits and back again without change to the value,"

n = ceil(1 + pmax*log10b)

With OP's machine, has p == 64 and b == 2 --> n == 21

Thus long doubles need to be converted to a decimal numbers with at least 21 significant digits, as text, to be convert back to the same long double - for the normal long double range.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    Using `LDBL_DECIMAL_DIG` does not narrow or simplify your answer because `DECIMAL_DIG` and `LDBL_DECIMAL_DIG` are the same. – Andrew McKinlay Dec 21 '19 at 21:19
  • @AndrewMcKinlay Fair point. The simplification is not having to refer to the _widest type_, but instead `long double` as it provides symmetry with `LDBL_DIG` Since C11, `DECIMAL_DIG` and `LDBL_DECIMAL_DIG` are the same given `long double` as the widest type. In future version, (or today with an implementation provided wide type), wider FP types may become standard, then `DECIMAL_DIG, LDBL_DECIMAL_DIG` can differ. – chux - Reinstate Monica Dec 21 '19 at 22:57
  • I didn't get why exactly binary base 2 leads to `LDBL_DIG != LDBL_DECIMAL_DIG`. Also: is binary base 2 always mean that `LDBL_DIG == LDBL_DECIMAL_DIG - 3` (and so on for other FP types)? – pmor Sep 28 '21 at 17:01
  • 1
    @pmor The difference is 2 (like IEEE `double`) or 3 (`float`). Why 2 or 3? --> Without getting into details, pow(2,2) <= 10/2 <= pow(2,3). A better answer deserves it own SO question. – chux - Reinstate Monica Sep 28 '21 at 17:06
  • 1
    @pmor [Printf width specifier to maintain precision of floating-point value](https://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value/19897395#19897395) may help. – chux - Reinstate Monica Sep 28 '21 at 17:20
  • @chux-ReinstateMonica [Question created](https://stackoverflow.com/questions/69449603/why-base-2-leads-to-flt-dig-flt-decimal-dig). – pmor Oct 05 '21 at 11:29
  • @chux-ReinstateMonica Additional questions created: [1](https://stackoverflow.com/questions/69449728/base-2-why-decimal-dig-dig-2-or-3), [2](https://stackoverflow.com/questions/69449894/if-c-standard-allows-double-float-then-why-it-requires-dbl-decimal-dig-and). – pmor Oct 05 '21 at 11:50
4

They're dealing with opposite round-trip directions.

  • DECIMAL_DIG is the number of decimal digits you need when converting from the largest floating point type to a decimal string and back to ensure that you get back the same value. (Of course for specific values you may be able to get by with fewer digits, but if you want a number of digits that works for any value, DECIMAL_DIG is it.) This is the long double -> decimal -> long double round trip.

  • LDBL_DIG is the number of decimal digits that will be reliably preserved when converting from decimal to long double and back. (For specific cases, more may be preserved, of course.) This is the decimal -> long double -> decimal round trip.

The text you quoted seems misleading and possibly utterly wrong, which is what you should expect from cppreference.com. It's a very bad site for information on C or C++.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • are you aware of another website with information on C? – Cubbi Sep 28 '16 at 14:13
  • Read the (draft, freely available, or purchase the final) standard. You can find it by searching for "n1570". – R.. GitHub STOP HELPING ICE Sep 28 '16 at 14:34
  • So "no". The standard is not nearly enough, by the way, especially when talking about floating-point stuff. – Cubbi Sep 28 '16 at 15:08
  • @Cubbi: The answer to this question can easily be found by a text-search of the standard for `DECIMAL_DIG`. This wiki might also be helpful: http://www.iso-9899.info/ – R.. GitHub STOP HELPING ICE Sep 28 '16 at 15:57
  • 1
    I agree that the standard defines these macros adequately, although it doesn’t explain them (your description is actually more user-friendly). Neither does cppreference right now,, but as any public wiki, it is subject to what the editors choose to write about. I think every macro from float.h deserves a separate page, with rationale, purpose, use cases and examples, but so does everything else. I edited the wording that was quoted here since it was indeed dumb. Still a long way to doing these macros justice, but several poorly-edited pages don't make the whole thing "bad". – Cubbi Sep 28 '16 at 16:50
  • I want to select your answer because of its brevity and closeness to the standard, but it does not demonstrate incorrect usage as asked in the original question, so it would be unfair to the other answers' authors. It also does not explain how the definitions I quoted in the question are misleading or wrong. – Andrew McKinlay Dec 21 '19 at 21:14
0

LBDL_DIG:

Number of decimal digits that can be rounded into a floating-point and back without change in the number of decimal digits.

and

Number of decimal digits, q, such that any floating-point number with q decimal digits can be rounded into a floating-point number with p radix b digits and back again without change to the q decimal digits.

enter image description here

DECIMAL_DIG:

Number of decimal digits that can be rounded into a floating-point type and back again to the same decimal digits, without loss in precision.

and

Number of decimal digits, n, such that any floating-point number in the widest supported floating type with pmax radix b digits can be rounded to a floating-point number with n decimal digits and back again without change to the value.

enter image description here

Answering to the OP's question: They are calculated in a different way. The formulas are attached above and are the reasons to the differences.

Source1 Source2 Useful url

xenteros
  • 15,586
  • 12
  • 56
  • 91
  • While your first definition quoted for `DECIMAL_DIG` corresponds to my definition, I do not see how it differs the first definition you quote for `LDBL_DIG`. "Number of decimal digits that can be rounded into a floating-point type and back again to the same decimal digits, without loss in precision" and "number of decimal digits that can be rounded into a floating-point and back without change in the number of decimal digits" sound the same to me. – Andrew McKinlay Dec 21 '19 at 21:09