Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printf width specifier to maintain precision of floating-point value

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 significant digits such that when scanning the string back in, the original floating point value is acquired?

For example, suppose I print a float to a precision of 2 decimal places:

float foobar = 0.9375; printf("%.2f", foobar);    // prints out 0.94 

When I scan the output 0.94, I have no standards-compliant guarantee that I'll get the original 0.9375 floating-point value back (in this example, I probably won't).

I would like a way tell printf to automatically print the floating-point value to the necessary number of significant digits to ensure that it can be scanned back to the original value passed to printf.

I could use some of the macros in float.h to derive the maximum width to pass to printf, but is there already a specifier to automatically print to the necessary number of significant digits -- or at least to the maximum width?

like image 468
Vilhelm Gray Avatar asked May 30 '13 15:05

Vilhelm Gray


People also ask

What do you use in printf () to display a floating point value?

we now see that the format specifier "%. 2f" tells the printf method to print a floating point value (the double, x, in this case) with 2 decimal places.

What is the difference between %F and %G in C?

They are both examples of floating point input/output. %g and %G are simplifiers of the scientific notation floats %e and %E. %g will take a number that could be represented as %f (a simple float or double) or %e (scientific notation) and return it as the shorter of the two.

What is %d %f %s in C?

%s refers to a string %d refers to an integer %c refers to a character. Therefore: %s%d%s%c\n prints the string "The first character in sting ", %d prints i, %s prints " is ", and %c prints str[0].


2 Answers

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 <float.h> 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 <float.h> 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 <float.h>.

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.

like image 95
chux - Reinstate Monica Avatar answered Sep 30 '22 05:09

chux - Reinstate Monica


The short answer to print floating point numbers losslessly (such that they can be read back in to exactly the same number, except NaN and Infinity):

  • If your type is float: use printf("%.9g", number).
  • If your type is double: use printf("%.17g", number).

Do NOT use %f, since that only specifies how many significant digits after the decimal and will truncate small numbers. For reference, the magic numbers 9 and 17 can be found in float.h which defines FLT_DECIMAL_DIG and DBL_DECIMAL_DIG.

like image 36
ccxvii Avatar answered Sep 30 '22 05:09

ccxvii