Printf width specifier to maintain precision of floating-point value

后端 未结 8 1380
萌比男神i
萌比男神i 2020-11-21 13:39

Is there a printf width specifier which can be applied to a floating point specifier that would automatically format the output to the necessary number of s

8条回答
  •  醉酒成梦
    2020-11-21 13:51

    I recommend @Jens Gustedt hexadecimal solution: use %a.

    OP wants “print with maximum precision (or at least to the most significant decimal)”.

    A simple example would be to print one seventh as in:

    #include 
    int Digs = DECIMAL_DIG;
    double OneSeventh = 1.0/7.0;
    printf("%.*e\n", Digs, OneSeventh);
    // 1.428571428571428492127e-01
    

    But let's dig deeper ...

    Mathematically, the answer is "0.142857 142857 142857 ...", but we are using finite precision floating point numbers. Let's assume IEEE 754 double-precision binary. So the OneSeventh = 1.0/7.0 results in the value below. Also shown are the preceding and following representable double floating point numbers.

    OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
    OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
    OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375
    

    Printing the exact decimal representation of a double has limited uses.

    C has 2 families of macros in to help us.
    The first set is the number of significant digits to print in a string in decimal so when scanning the string back, we get the original floating point. There are shown with the C spec's minimum value and a sample C11 compiler.

    FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
    DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
    LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
    DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)
    

    The second set is the number of significant digits a string may be scanned into a floating point and then the FP printed, still retaining the same string presentation. There are shown with the C spec's minimum value and a sample C11 compiler. I believe available pre-C99.

    FLT_DIG   6, 6 (float)
    DBL_DIG  10, 15 (double)
    LDBL_DIG 10, 18 (long double)
    

    The first set of macros seems to meet OP's goal of significant digits. But that macro is not always available.

    #ifdef DBL_DECIMAL_DIG
      #define OP_DBL_Digs (DBL_DECIMAL_DIG)
    #else  
      #ifdef DECIMAL_DIG
        #define OP_DBL_Digs (DECIMAL_DIG)
      #else  
        #define OP_DBL_Digs (DBL_DIG + 3)
      #endif
    #endif
    

    The "+ 3" was the crux of my previous answer. Its centered on if knowing the round-trip conversion string-FP-string (set #2 macros available C89), how would one determine the digits for FP-string-FP (set #1 macros available post C89)? In general, add 3 was the result.

    Now how many significant digits to print is known and driven via .

    To print N significant decimal digits one may use various formats.

    With "%e", the precision field is the number of digits after the lead digit and decimal point. So - 1 is in order. Note: This -1 is not in the initial int Digs = DECIMAL_DIG;

    printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
    // 1.4285714285714285e-01
    

    With "%f", the precision field is the number of digits after the decimal point. For a number like OneSeventh/1000000.0, one would need OP_DBL_Digs + 6 to see all the significant digits.

    printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
    // 0.14285714285714285
    printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
    // 0.00000014285714285714285
    

    Note: Many are use to "%f". That displays 6 digits after the decimal point; 6 is the display default, not the precision of the number.

提交回复
热议问题