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?
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.
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.
If you are only interested in the bit (resp hex pattern) you could use the %a format. This guarantees you:
The
default precision suffices for an exact representation of the value if an exact representation in base 2 exists and otherwise is sufficiently large to distinguish values of type double.
I'd have to add that this is only available since C99.
No, there is no such printf width specifier to print floating-point with maximum precision. Let me explain why.
The maximum precision of float and double is variable, and dependent on the actual value of the float or double.
Recall float and double are stored in sign.exponent.mantissa format. This means that there are many more bits used for the fractional component for small numbers than for big numbers.
For example, float can easily distinguish between 0.0 and 0.1.
float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000
But float has no idea of the difference between 1e27 and 1e27 + 0.1.
r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000
This is because all the precision (which is limited by the number of mantissa bits) is used up for the large part of the number, left of the decimal.
The %.f modifier just says how many decimal values you want to print from the float number as far as formatting goes. The fact that the accuracy available depends on the size of the number is up to you as the programmer to handle. printf can't/doesn't handle that for you.
Simply use the macros from <float.h> and the variable-width conversion specifier (".*"):
float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);
In one of my comments to an answer I lamented that I've long wanted some way to print all the significant digits in a floating point value in decimal form, in much the same way the as the question asks. Well I finally sat down and wrote it. It's not quite perfect, and this is demo code that prints additional information, but it mostly works for my tests. Please let me know if you (i.e. anyone) would like a copy of the whole wrapper program which drives it for testing.
static unsigned int
ilog10(uintmax_t v);
/*
* Note: As presented this demo code prints a whole line including information
* about how the form was arrived with, as well as in certain cases a couple of
* interesting details about the number, such as the number of decimal places,
* and possibley the magnitude of the value and the number of significant
* digits.
*/
void
print_decimal(double d)
{
size_t sigdig;
int dplaces;
double flintmax;
/*
* If we really want to see a plain decimal presentation with all of
* the possible significant digits of precision for a floating point
* number, then we must calculate the correct number of decimal places
* to show with "%.*f" as follows.
*
* This is in lieu of always using either full on scientific notation
* with "%e" (where the presentation is always in decimal format so we
* can directly print the maximum number of significant digits
* supported by the representation, taking into acount the one digit
* represented by by the leading digit)
*
* printf("%1.*e", DBL_DECIMAL_DIG - 1, d)
*
* or using the built-in human-friendly formatting with "%g" (where a
* '*' parameter is used as the number of significant digits to print
* and so we can just print exactly the maximum number supported by the
* representation)
*
* printf("%.*g", DBL_DECIMAL_DIG, d)
*
*
* N.B.: If we want the printed result to again survive a round-trip
* conversion to binary and back, and to be rounded to a human-friendly
* number, then we can only print DBL_DIG significant digits (instead
* of the larger DBL_DECIMAL_DIG digits).
*
* Note: "flintmax" here refers to the largest consecutive integer
* that can be safely stored in a floating point variable without
* losing precision.
*/
#ifdef PRINT_ROUND_TRIP_SAFE
# ifdef DBL_DIG
sigdig = DBL_DIG;
# else
sigdig = ilog10(uipow(FLT_RADIX, DBL_MANT_DIG - 1));
# endif
#else
# ifdef DBL_DECIMAL_DIG
sigdig = DBL_DECIMAL_DIG;
# else
sigdig = (size_t) lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX))) + 1;
# endif
#endif
flintmax = pow((double) FLT_RADIX, (double) DBL_MANT_DIG); /* xxx use uipow() */
if (d == 0.0) {
printf("z = %.*s\n", (int) sigdig + 1, "0.000000000000000000000"); /* 21 */
} else if (fabs(d) >= 0.1 &&
fabs(d) <= flintmax) {
dplaces = (int) (sigdig - (size_t) lrint(ceil(log10(ceil(fabs(d))))));
if (dplaces < 0) {
/* XXX this is likely never less than -1 */
/*
* XXX the last digit is not significant!!! XXX
*
* This should also be printed with sprintf() and edited...
*/
printf("R = %.0f [%d too many significant digits!!!, zero decimal places]\n", d, abs(dplaces));
} else if (dplaces == 0) {
/*
* The decimal fraction here is not significant and
* should always be zero (XXX I've never seen this)
*/
printf("R = %.0f [zero decimal places]\n", d);
} else {
if (fabs(d) == 1.0) {
/*
* This is a special case where the calculation
* is off by one because log10(1.0) is 0, but
* we still have the leading '1' whole digit to
* count as a significant digit.
*/
#if 0
printf("ceil(1.0) = %f, log10(ceil(1.0)) = %f, ceil(log10(ceil(1.0))) = %f\n",
ceil(fabs(d)), log10(ceil(fabs(d))), ceil(log10(ceil(fabs(d)))));
#endif
dplaces--;
}
/* this is really the "useful" range of %f */
printf("r = %.*f [%d decimal places]\n", dplaces, d, dplaces);
}
} else {
if (fabs(d) < 1.0) {
int lz;
lz = abs((int) lrint(floor(log10(fabs(d)))));
/* i.e. add # of leading zeros to the precision */
dplaces = (int) sigdig - 1 + lz;
printf("f = %.*f [%d decimal places]\n", dplaces, d, dplaces);
} else { /* d > flintmax */
size_t n;
size_t i;
char *df;
/*
* hmmmm... the easy way to suppress the "invalid",
* i.e. non-significant digits is to do a string
* replacement of all dgits after the first
* DBL_DECIMAL_DIG to convert them to zeros, and to
* round the least significant digit.
*/
df = malloc((size_t) 1);
n = (size_t) snprintf(df, (size_t) 1, "%.1f", d);
n++; /* for the NUL */
df = realloc(df, n);
(void) snprintf(df, n, "%.1f", d);
if ((n - 2) > sigdig) {
/*
* XXX rounding the integer part here is "hard"
* -- we would have to convert the digits up to
* this point back into a binary format and
* round that value appropriately in order to
* do it correctly.
*/
if (df[sigdig] >= '5' && df[sigdig] <= '9') {
if (df[sigdig - 1] == '9') {
/*
* xxx fixing this is left as
* an exercise to the reader!
*/
printf("F = *** failed to round integer part at the least significant digit!!! ***\n");
free(df);
return;
} else {
df[sigdig - 1]++;
}
}
for (i = sigdig; df[i] != '.'; i++) {
df[i] = '0';
}
} else {
i = n - 1; /* less the NUL */
if (isnan(d) || isinf(d)) {
sigdig = 0; /* "nan" or "inf" */
}
}
printf("F = %.*s. [0 decimal places, %lu digits, %lu digits significant]\n",
(int) i, df, (unsigned long int) i, (unsigned long int) sigdig);
free(df);
}
}
return;
}
static unsigned int
msb(uintmax_t v)
{
unsigned int mb = 0;
while (v >>= 1) { /* unroll for more speed... (see ilog2()) */
mb++;
}
return mb;
}
static unsigned int
ilog10(uintmax_t v)
{
unsigned int r;
static unsigned long long int const PowersOf10[] =
{ 1LLU, 10LLU, 100LLU, 1000LLU, 10000LLU, 100000LLU, 1000000LLU,
10000000LLU, 100000000LLU, 1000000000LLU, 10000000000LLU,
100000000000LLU, 1000000000000LLU, 10000000000000LLU,
100000000000000LLU, 1000000000000000LLU, 10000000000000000LLU,
100000000000000000LLU, 1000000000000000000LLU,
10000000000000000000LLU };
if (!v) {
return ~0U;
}
/*
* By the relationship "log10(v) = log2(v) / log2(10)", we need to
* multiply "log2(v)" by "1 / log2(10)", which is approximately
* 1233/4096, or (1233, followed by a right shift of 12).
*
* Finally, since the result is only an approximation that may be off
* by one, the exact value is found by subtracting "v < PowersOf10[r]"
* from the result.
*/
r = ((msb(v) * 1233) >> 12) + 1;
return r - (v < PowersOf10[r]);
}
I run a small experiment to verify that printing with DBL_DECIMAL_DIG does indeed exactly preserve the number's binary representation. It turned out that for the compilers and C libraries I tried, DBL_DECIMAL_DIG is indeed the number of digits required, and printing with even one digit less creates a significant problem.
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union {
short s[4];
double d;
} u;
void
test(int digits)
{
int i, j;
char buff[40];
double d2;
int n, num_equal, bin_equal;
srand(17);
n = num_equal = bin_equal = 0;
for (i = 0; i < 1000000; i++) {
for (j = 0; j < 4; j++)
u.s[j] = (rand() << 8) ^ rand();
if (isnan(u.d))
continue;
n++;
sprintf(buff, "%.*g", digits, u.d);
sscanf(buff, "%lg", &d2);
if (u.d == d2)
num_equal++;
if (memcmp(&u.d, &d2, sizeof(double)) == 0)
bin_equal++;
}
printf("Tested %d values with %d digits: %d found numericaly equal, %d found binary equal\n", n, digits, num_equal, bin_equal);
}
int
main()
{
test(DBL_DECIMAL_DIG);
test(DBL_DECIMAL_DIG - 1);
return 0;
}
I run this with Microsoft's C compiler 19.00.24215.1 and gcc version 7.4.0 20170516 (Debian 6.3.0-18+deb9u1). Using one less decimal digit halves the number of numbers that compare exactly equal. (I also verified that rand() as used indeed produces about one million different numbers.) Here are the detailed results.
Microsoft C
Tested 999507 values with 17 digits: 999507 found numericaly equal, 999507 found binary equal
Tested 999507 values with 16 digits: 545389 found numericaly equal, 545389 found binary equal
GCC
Tested 999485 values with 17 digits: 999485 found numericaly equal, 999485 found binary equal
Tested 999485 values with 16 digits: 545402 found numericaly equal, 545402 found binary equal
To my knowledge, there is a well diffused algorithm allowing to output to the necessary number of significant digits such that when scanning the string back in, the original floating point value is acquired in dtoa.c written by David Gay, which is available here on Netlib (see also the associated paper). This code is used e.g. in Python, MySQL, Scilab, and many others.
Related
Suppose I have a floating-point value of type float or double (i.e. 32 or 64 bits on typical machines). I want to print this value as text (e.g. to the standard output stream), and then later, in some other process, scan it back in - with fscanf() if I'm using C, or perhaps with istream::operator>>() if I'm using C++. But - I need the scanned float to end up being exactly, identical to the original value (up to equivalent representations of the same value). Also, the printed value should be easily readable - to a human - as floating-point, i.e. I don't want to print 0x42355316 and reinterpret that as a 32-bit float.
How should I do this? I'm assuming the standard library of (C and C++) won't be sufficient, but perhaps I'm wrong. I suppose that a sufficient number of decimal digits might be able to guarantee an error that's underneath the precision threshold - but that's not the same as guaranteeing the rounding/truncation will happen just the way I want it.
Notes:
The scanning does not having to be perfectly accurate w.r.t. the value it scans, only the original value.
If it makes it easier, you may assume the value is a number and is not infinity.
denormal support is desired but not required; still if we get a denormal, failure should be conspicuous.
First, you should use the %a format with fprintf and fscanf. This is what it was designed for, and the C standard requires it to work (reproduce the original number) if the implementation uses binary floating-point.
Failing that, you should print a float with at least FLT_DECIMAL_DIG significant digits and a double with at least DBL_DECIMAL_DIG significant digits. Those constants are defined in <float.h> and are defined:
… number of decimal digits, n, such that any floating-point number with p radix b digits can be rounded to a floating-point number with n decimal digits and back again without change to the value,… [b is the base used for the floating-point format, defined in FLT_RADIX, and p is the number of base-b digits in the format.]
For example:
printf("%.*g\n", FLT_DECIMAL_DIG, 1.f/3);
or:
#define QuoteHelper(x) #x
#define Quote(x) QuoteHelper(x)
…
printf("%." Quote(FLT_DECIMAL_DIG) "g\n", 1.f/3);
In C++, these constants are defined in <limits> as std::numeric_limits<Type>::max_digits10, where Type is float or double or another floating-point type.
Note that the C standard only recommends that such a round-trip through a decimal numeral work; it does not require it. For example, C 2018 5.2.4.2.2 15 says, under the heading “Recommended practice”:
Conversion from (at least) double to decimal with DECIMAL_DIG digits and back should be the identity function. [DECIMAL_DIG is the equivalent of FLT_DECIMAL_DIG or DBL_DECIMAL_DIG for the widest floating-point format supported in the implementation.]
In contrast, if you use %a, and FLT_RADIX is a power of two (meaning the implementation uses a floating-point base that is two, 16, or another power of two), then C standard requires that the result of scanning the numeral produced with %a equals the original number.
I need the scanned float to end up being exactly, identical to the original value.
As already pointed out in the other answers, that can be achieved with the %a format specifier.
Also, the printed value should be easily readable - to a human - as floating-point, i.e. I don't want to print 0x42355316 and reinterpret that as a 32-bit float.
That's more tricky and subjective. The first part of the string that %a produces is in fact a fraction composed by hexadecimal digits, so that an output like 0x1.4p+3 may take some time to be parsed as 10 by a human reader.
An option could be to print all the decimal digits needed to represent the floating-point value, but there may be a lot of them. Consider, for example the value 0.1, its closest representation as a 64-bit float may be
0x1.999999999999ap-4 == 0.1000000000000000055511151231257827021181583404541015625
While printf("%.*lf\n", DBL_DECIMAL_DIG, 01); (see e.g. Eric's answer) would print
0.10000000000000001 // If DBL_DECIMAL_DIG == 17
My proposal is somewhere in the middle. Similarly to what %a does, we can exactly represent any floating-point value with radix 2 as a fraction multiplied by 2 raised to some integer power. We can transform that fraction into a whole number (increasing the exponent accordingly) and print it as a decimal value.
0x1.999999999999ap-4 --> 1.999999999999a16 * 2-4 --> 1999999999999a16 * 2-56
--> 720575940379279410 * 2-56
That whole number has a limited number of digits (it's < 253), but the result it's still an exact representation of the original double value.
The following snippet is a proof of concept, without any check for corner cases. The format specifier %a separates the mantissa and the exponent with a p character (as in "... multiplied by two raised to the Power of..."), I'll use a q instead, for no particular reason other than using a different symbol.
The value of the mantissa will also be reduced (and the exponent raised accordingly), removing all the trailing zero-bits. The idea beeing that 5q+1 (parsed as 510 * 21) should be more "easily" identified as 10, rather than 2814749767106560q-48.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void to_my_format(double x, char *str)
{
int exponent;
double mantissa = frexp(x, &exponent);
long long m = 0;
if ( mantissa ) {
exponent -= 52;
m = (long long)scalbn(mantissa, 52);
// A reduced mantissa should be more readable
while (m && m % 2 == 0) {
++exponent;
m /= 2;
}
}
sprintf(str, "%lldq%+d", m, exponent);
// ^
// Here 'q' is used to separate the mantissa from the exponent
}
double from_my_format(char const *str)
{
char *end;
long long mantissa = strtoll(str, &end, 10);
long exponent = strtol(str + (end - str + 1), &end, 10);
return scalbn(mantissa, exponent);
}
int main(void)
{
double tests[] = { 1, 0.5, 2, 10, -256, acos(-1), 1000000, 0.1, 0.125 };
size_t n = (sizeof tests) / (sizeof *tests);
char num[32];
for ( size_t i = 0; i < n; ++i ) {
to_my_format(tests[i], num);
double x = from_my_format(num);
printf("%22s%22a ", num, tests[i]);
if ( tests[i] != x )
printf(" *** %22a *** Round-trip failed\n", x);
else
printf("%58.55g\n", x);
}
return 0;
}
Testable here.
Generally, the improvement in readability is admitedly little to none, surely a matter of opinion.
You can use the %a format specifier to print the value as hexadecimal floating point. Note that this is not the same as reinterpreting the float as an integer and printing the integer value.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
float x;
scanf("%f", &x);
printf("x=%.7f\n", x);
char str[20];
sprintf(str, "%a", x);
printf("str=%s\n", str);
float y;
sscanf(str, "%f", &y);
printf("y=%.7f\n", y);
printf("x==y: %d\n", (x == y));
return 0;
}
With an input of 4, this outputs:
x=4.0000000
str=0x1p+2
y=4.0000000
x==y: 1
With an input of 3.3, this outputs:
x=3.3000000
str=0x1.a66666p+1
y=3.3000000
x==y: 1
As you can see from the output, the %a format specifier prints in exponential format with the significand in hex and the exponent in decimal. This format can then be converted directly back to the exact same value as demonstrated by the equality check.
I'm trying to print out the decimal expansion of a rational number in C. The problem I have is that when I divide the numerator by the denominator I lose precision. C rounds up the repeating part when I don't want it to.
For example, 1562/4995 = 0.3127127127... but in my program I get 1562/4995 = 0.312713. As you can see a part of the number that I need has been lost.
Is there a way to specify C to preserve a higher level of decimal precision?
I have tried to declare the result as a double, long double and float. I also tried to split the expansion into 2 integers seperated by a '.'
However both methods haven't been successful.
int main() {
int numerator, denominator;
numerator = 1562;
denominator = 4995;
double result;
result = (double) numerator / (double) denominator;
printf("%f\n", result);
return 0;
}
I expected the output to be 1562/4995 = 0.3127127127... but the actual output is 1562/4995 = 0.312713
The %f format specifier to printf shows 6 digits after the decimal point by default. If you want to show more digits, use a precision specifier:
printf("%.10f\n", result);
Also, the double type can only accurately store roughly 16 decimal digits of precision.
You need to change your output format, like this:
printf("%.10lf\n", result);
Note two things:
The value after the . specifies the decimal precision required (10 decimal places, here).
Note that I have added an l before the f to explicitly state that the argument is a double rather than a (single-precision) float.
EDIT: Note that, for the printf function, it is not strictly necessary to include the l modifier. However, when you come to use the 'corresponding' scanf function for input, it's absence will generate a warning and (probably) undefined behaviour:
scanf("%f", &result); // Not correct
scanf("%lf", &result); // Correct
If printing is all you want to do, you can do this with the same long division you were taught in elementary school:
#include <stdio.h>
/* Print the decimal representation of N/D with up to
P digits after the decimal point.
*/
#define P 60
static void PrintDecimal(unsigned N, unsigned D)
{
// Print the integer portion.
printf("%u.", N/D);
// Take the remainder.
N %= D;
for (int i = 0; i < P && N; ++i)
{
// Move to next digit position and print next digit.
N *= 10;
printf("%u", N/D);
// Take the remainder.
N %= D;
}
}
int main(void)
{
PrintDecimal(1562, 4995);
putchar('\n');
}
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?
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.
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.
If you are only interested in the bit (resp hex pattern) you could use the %a format. This guarantees you:
The
default precision suffices for an exact representation of the value if an exact representation in base 2 exists and otherwise is sufficiently large to distinguish values of type double.
I'd have to add that this is only available since C99.
No, there is no such printf width specifier to print floating-point with maximum precision. Let me explain why.
The maximum precision of float and double is variable, and dependent on the actual value of the float or double.
Recall float and double are stored in sign.exponent.mantissa format. This means that there are many more bits used for the fractional component for small numbers than for big numbers.
For example, float can easily distinguish between 0.0 and 0.1.
float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000
But float has no idea of the difference between 1e27 and 1e27 + 0.1.
r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000
This is because all the precision (which is limited by the number of mantissa bits) is used up for the large part of the number, left of the decimal.
The %.f modifier just says how many decimal values you want to print from the float number as far as formatting goes. The fact that the accuracy available depends on the size of the number is up to you as the programmer to handle. printf can't/doesn't handle that for you.
Simply use the macros from <float.h> and the variable-width conversion specifier (".*"):
float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);
In one of my comments to an answer I lamented that I've long wanted some way to print all the significant digits in a floating point value in decimal form, in much the same way the as the question asks. Well I finally sat down and wrote it. It's not quite perfect, and this is demo code that prints additional information, but it mostly works for my tests. Please let me know if you (i.e. anyone) would like a copy of the whole wrapper program which drives it for testing.
static unsigned int
ilog10(uintmax_t v);
/*
* Note: As presented this demo code prints a whole line including information
* about how the form was arrived with, as well as in certain cases a couple of
* interesting details about the number, such as the number of decimal places,
* and possibley the magnitude of the value and the number of significant
* digits.
*/
void
print_decimal(double d)
{
size_t sigdig;
int dplaces;
double flintmax;
/*
* If we really want to see a plain decimal presentation with all of
* the possible significant digits of precision for a floating point
* number, then we must calculate the correct number of decimal places
* to show with "%.*f" as follows.
*
* This is in lieu of always using either full on scientific notation
* with "%e" (where the presentation is always in decimal format so we
* can directly print the maximum number of significant digits
* supported by the representation, taking into acount the one digit
* represented by by the leading digit)
*
* printf("%1.*e", DBL_DECIMAL_DIG - 1, d)
*
* or using the built-in human-friendly formatting with "%g" (where a
* '*' parameter is used as the number of significant digits to print
* and so we can just print exactly the maximum number supported by the
* representation)
*
* printf("%.*g", DBL_DECIMAL_DIG, d)
*
*
* N.B.: If we want the printed result to again survive a round-trip
* conversion to binary and back, and to be rounded to a human-friendly
* number, then we can only print DBL_DIG significant digits (instead
* of the larger DBL_DECIMAL_DIG digits).
*
* Note: "flintmax" here refers to the largest consecutive integer
* that can be safely stored in a floating point variable without
* losing precision.
*/
#ifdef PRINT_ROUND_TRIP_SAFE
# ifdef DBL_DIG
sigdig = DBL_DIG;
# else
sigdig = ilog10(uipow(FLT_RADIX, DBL_MANT_DIG - 1));
# endif
#else
# ifdef DBL_DECIMAL_DIG
sigdig = DBL_DECIMAL_DIG;
# else
sigdig = (size_t) lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX))) + 1;
# endif
#endif
flintmax = pow((double) FLT_RADIX, (double) DBL_MANT_DIG); /* xxx use uipow() */
if (d == 0.0) {
printf("z = %.*s\n", (int) sigdig + 1, "0.000000000000000000000"); /* 21 */
} else if (fabs(d) >= 0.1 &&
fabs(d) <= flintmax) {
dplaces = (int) (sigdig - (size_t) lrint(ceil(log10(ceil(fabs(d))))));
if (dplaces < 0) {
/* XXX this is likely never less than -1 */
/*
* XXX the last digit is not significant!!! XXX
*
* This should also be printed with sprintf() and edited...
*/
printf("R = %.0f [%d too many significant digits!!!, zero decimal places]\n", d, abs(dplaces));
} else if (dplaces == 0) {
/*
* The decimal fraction here is not significant and
* should always be zero (XXX I've never seen this)
*/
printf("R = %.0f [zero decimal places]\n", d);
} else {
if (fabs(d) == 1.0) {
/*
* This is a special case where the calculation
* is off by one because log10(1.0) is 0, but
* we still have the leading '1' whole digit to
* count as a significant digit.
*/
#if 0
printf("ceil(1.0) = %f, log10(ceil(1.0)) = %f, ceil(log10(ceil(1.0))) = %f\n",
ceil(fabs(d)), log10(ceil(fabs(d))), ceil(log10(ceil(fabs(d)))));
#endif
dplaces--;
}
/* this is really the "useful" range of %f */
printf("r = %.*f [%d decimal places]\n", dplaces, d, dplaces);
}
} else {
if (fabs(d) < 1.0) {
int lz;
lz = abs((int) lrint(floor(log10(fabs(d)))));
/* i.e. add # of leading zeros to the precision */
dplaces = (int) sigdig - 1 + lz;
printf("f = %.*f [%d decimal places]\n", dplaces, d, dplaces);
} else { /* d > flintmax */
size_t n;
size_t i;
char *df;
/*
* hmmmm... the easy way to suppress the "invalid",
* i.e. non-significant digits is to do a string
* replacement of all dgits after the first
* DBL_DECIMAL_DIG to convert them to zeros, and to
* round the least significant digit.
*/
df = malloc((size_t) 1);
n = (size_t) snprintf(df, (size_t) 1, "%.1f", d);
n++; /* for the NUL */
df = realloc(df, n);
(void) snprintf(df, n, "%.1f", d);
if ((n - 2) > sigdig) {
/*
* XXX rounding the integer part here is "hard"
* -- we would have to convert the digits up to
* this point back into a binary format and
* round that value appropriately in order to
* do it correctly.
*/
if (df[sigdig] >= '5' && df[sigdig] <= '9') {
if (df[sigdig - 1] == '9') {
/*
* xxx fixing this is left as
* an exercise to the reader!
*/
printf("F = *** failed to round integer part at the least significant digit!!! ***\n");
free(df);
return;
} else {
df[sigdig - 1]++;
}
}
for (i = sigdig; df[i] != '.'; i++) {
df[i] = '0';
}
} else {
i = n - 1; /* less the NUL */
if (isnan(d) || isinf(d)) {
sigdig = 0; /* "nan" or "inf" */
}
}
printf("F = %.*s. [0 decimal places, %lu digits, %lu digits significant]\n",
(int) i, df, (unsigned long int) i, (unsigned long int) sigdig);
free(df);
}
}
return;
}
static unsigned int
msb(uintmax_t v)
{
unsigned int mb = 0;
while (v >>= 1) { /* unroll for more speed... (see ilog2()) */
mb++;
}
return mb;
}
static unsigned int
ilog10(uintmax_t v)
{
unsigned int r;
static unsigned long long int const PowersOf10[] =
{ 1LLU, 10LLU, 100LLU, 1000LLU, 10000LLU, 100000LLU, 1000000LLU,
10000000LLU, 100000000LLU, 1000000000LLU, 10000000000LLU,
100000000000LLU, 1000000000000LLU, 10000000000000LLU,
100000000000000LLU, 1000000000000000LLU, 10000000000000000LLU,
100000000000000000LLU, 1000000000000000000LLU,
10000000000000000000LLU };
if (!v) {
return ~0U;
}
/*
* By the relationship "log10(v) = log2(v) / log2(10)", we need to
* multiply "log2(v)" by "1 / log2(10)", which is approximately
* 1233/4096, or (1233, followed by a right shift of 12).
*
* Finally, since the result is only an approximation that may be off
* by one, the exact value is found by subtracting "v < PowersOf10[r]"
* from the result.
*/
r = ((msb(v) * 1233) >> 12) + 1;
return r - (v < PowersOf10[r]);
}
I run a small experiment to verify that printing with DBL_DECIMAL_DIG does indeed exactly preserve the number's binary representation. It turned out that for the compilers and C libraries I tried, DBL_DECIMAL_DIG is indeed the number of digits required, and printing with even one digit less creates a significant problem.
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union {
short s[4];
double d;
} u;
void
test(int digits)
{
int i, j;
char buff[40];
double d2;
int n, num_equal, bin_equal;
srand(17);
n = num_equal = bin_equal = 0;
for (i = 0; i < 1000000; i++) {
for (j = 0; j < 4; j++)
u.s[j] = (rand() << 8) ^ rand();
if (isnan(u.d))
continue;
n++;
sprintf(buff, "%.*g", digits, u.d);
sscanf(buff, "%lg", &d2);
if (u.d == d2)
num_equal++;
if (memcmp(&u.d, &d2, sizeof(double)) == 0)
bin_equal++;
}
printf("Tested %d values with %d digits: %d found numericaly equal, %d found binary equal\n", n, digits, num_equal, bin_equal);
}
int
main()
{
test(DBL_DECIMAL_DIG);
test(DBL_DECIMAL_DIG - 1);
return 0;
}
I run this with Microsoft's C compiler 19.00.24215.1 and gcc version 7.4.0 20170516 (Debian 6.3.0-18+deb9u1). Using one less decimal digit halves the number of numbers that compare exactly equal. (I also verified that rand() as used indeed produces about one million different numbers.) Here are the detailed results.
Microsoft C
Tested 999507 values with 17 digits: 999507 found numericaly equal, 999507 found binary equal
Tested 999507 values with 16 digits: 545389 found numericaly equal, 545389 found binary equal
GCC
Tested 999485 values with 17 digits: 999485 found numericaly equal, 999485 found binary equal
Tested 999485 values with 16 digits: 545402 found numericaly equal, 545402 found binary equal
To my knowledge, there is a well diffused algorithm allowing to output to the necessary number of significant digits such that when scanning the string back in, the original floating point value is acquired in dtoa.c written by David Gay, which is available here on Netlib (see also the associated paper). This code is used e.g. in Python, MySQL, Scilab, and many others.
I am trying to get the amount of decimals a number has in c: 0.0001 -> 4 decimals, 3,54235 -> 5 decimals, and so on (If you don't get it, the number of numbers behind the comma.) our teacher sais it can be done in two ways, using a string and not using a string. I figured i would go ahead not using a string because I have NO experiance with strings.
So this is what I came up with
int funzione1(float decimals){
int x=1,c=0,y=1;
while (x!=0){
if((decimals - y) > 0){
y = y / 10;
c++;
}else{
decimals = decimals - y;
}
if(decimals == 0)
x=0;
}
return c-1;
}
When calling the function it should return the amount of decimals I figured, but it does not, actually it gets stuck in an infinite loop.
the Idea behind this code was to for every number in the "string" of numbers to get them to 0 and then check if the total number was 0
3.456 c=0
0.456 c=1
0.056 c=2
0.006 c=3
0.000 return c
But That leaves me with two problems 1 how to detirmine tha amount of numbers before the comma for like 5564.34234 this code will not work because it will count to 8 before the full number is a solid 0. and therefor not return the right number of decimals.2. the code I designed isn't working. Just gets stuck in an infinite loop. I don't know where the infiniteness of the loop is created.
How do i get this code to work?
PS. I found this article about this problem in Java: How to find out how many decimals a number has? but it is using strings and I would not like that because of the fact that I don't know how to use strings.
edit: Here is another piece of code i tried and which faild really bad givving an output of 50 when you enter a number higher than 1 and 0 if the number is lower than 0(I don't get it, not a little bit) anyway here is the code:
int funzione1(float decimals){
int i=0;
while(decimals!=((int)decimals)){
i++;
decimals=decimals*10;
}
return i;
}
If you don't care about rounding then you don't need to count the number of decimal places, you can just count the number of binary places. This is because 10 contains 2 as a factor exactly once so 10^n and 2^n have the same number of 2s as factors. The fastest way to count the number of binary places is to get the exponent of the floating point number.
e.g. binary 0.001 takes 3 decimal places to represent 0.125, 0.0001 takes 4 0.0625.
You can either get the fractional part of the value and keep multiplying by 2 and removing the integer as people have suggested doing with 10 (it will give you the same answer).
Or you can have a bit more fun over optimising the solution (the places function does most of the work):
#include <math.h>
int saturateLeft (unsigned int n) {
n |= (n << 1);
n |= (n << 2);
n |= (n << 4);
n |= (n << 8);
n |= (n << 16);
return n;
}
int NumberOfSetBits(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
int places (double num) {
int exponent;
float mantissa = frexp (num, &exponent);
/* The answer we are looking for is given by the
(number of bits used by mantissa) - the exponent.
*/
unsigned intMantissa = scalbnf (mantissa, 32);
/* Could also be got by doing:
intMantissa = *(unsigned *)&mantissa << 9;
*/
/* To work out how many bits the mantissa covered we
need no gaps in the mantissa, this removes any gaps.
*/
intMantissa = saturateLeft (intMantissa);
int bitCount = NumberOfSetBits (intMantissa);
/* bitCount could also be found like this:
intMantissa = ~intMantissa;
int bitCount = 32 - ilogb (intMantissa) - 1;
*/
int result = bitCount - exponent;
if (result < 0)
return 0;
return result;
}
The bitCounting algorithm was found here.
Your best bet would be to read the input as string and just count the digits after '.'. Floating point numbers are not exact representation i.e. the decimal values are stored in binary internally and may not exactly represent the true decimal value. However every binary representation is some decimal number with finite digits.
Have a look at this answer in SO.
Here's an idea:
Start with a floating point number, say a = 3.0141589
Make the part before the decimal point 0 by subtracting the integral part, leaving 0.0141589
In a loop, multiply a by 10 and save the integral part, this gives you a list of digits from 0 to 9.
From this list, derive the number of decimals
There are some interesting details in this algorithm for you to find out, and I won't spoil the fun or surprises waiting for you.
Consider my string-based solution:
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <math.h>
#include <float.h>
int countDecimals(double x)
{
int cnt;
char * ptr;
char str[20] = {0};
// take fractional part from x
double ip;
double fp = modf (x , &ip);
// printf("%lg + %.15lg\n", ip, fp); // uncomment for debugging
// to be sure that Decimal-point character is '.'
setlocale(LC_NUMERIC, "C");
// make string from number's fractional part asking maximum digits (DBL_DIG)
sprintf(str,"%.*lg", DBL_DIG, fp);
// find point
if( ptr = strchr(str, '.') )
{
// calculate length of string after point
// length - (point position from beginning) - point = number of decimals
cnt = strlen(str) - (ptr - str) - 1;
}
else
{
cnt = 0;
}
return cnt;
}
int main (void)
{
double fn;
printf("Enter a float number: ");
scanf("%lf", &fn);
printf("Your number has %d decimals.\n" , countDecimals(fn) );
return 0;
}
But you should remember about possible rounding and accuracy errors.
Note: I have used double type, for float function modff should be used instead of modf, and FLT_DIG instead of DBL_DIG
I need to convert a floating point number to the following nonstandard format:
"a floating point number F (in standard decimal exponential format), followed by the string
times 10 to the power,
followed by an integer K."
Is there a way to extract the exponent from e% or a function similar to frexp()?
E.g.: 3349.25 should be "3.34925 times 10 to the power 3"
You can implement a decimal equivalent of frexp yourself quite easily. The code you need looks something like this:
int exponent = (int)log10(fabs(d));
double mantissa = d / pow(10, exponent);
printf("%f X 10^%d\n", mantissa, exponent);
First we determine the exponent by taking the base-10 logarithm of the absolute value of your number. (We need fabs because log10 requires a positive argument.) The cast rounds towards zero, which is, conveniently, what we need. Then we normalize the mantissa by dividing.
This doesn't handle d==0 (or infinity or NaN), the division will introduce some error into the result, and I haven't tested it with small numbers or negative numbers, but this should give you something to start from.
I like the OP approach to let printf(), using the "%e" format, to do the heavy lifting.
There are many issues with double to string conversion that existing functions already handle nicely. (INF, Nan, -0, rounding, negatives). OP use of frexp(), power() typically have troubles near values a power of 10 do to rounding and and finite precision.
OP is fuzzy on how much precision given only 1 example. power(0.5, 100) would need 100 digits. Let's limit it to DBL_DIG, rather than use the %e default of 6. (which is really 7 digits, just 6 after the DP.)
char *Heyhey_Notation(char *dest, double x) {
static const char Times[] = " times 10 to the power ";
char buffer[3 + DBL_DIG + sizeof(Times) + 20 + 1];
sprintf(buffer, "%.*e", DBL_DIG, x);
char *e = strchr(buffer, 'e'); // find exponent position
if (e) {
char *zero = e;
while (zero[-1] == '0') zero--;
*zero = '\0'; // OP wants excess zeros trimmed.
int power = atoi(&e[1]); // trim excess zeros by converting to int
sprintf(dest, "%s%s%d", buffer, Times, power);
}
else {
strcpy(dest, buffer);
}
return dest;
}
int main() {
char buf[100];
puts(Heyhey_Notation(buf, 3349.25));
puts(Heyhey_Notation(buf, -0.0));
puts(Heyhey_Notation(buf, 123));
puts(Heyhey_Notation(buf, -1234567890.0/9999999999.0));
puts(Heyhey_Notation(buf, -1/0.0));
puts(Heyhey_Notation(buf, atof("nan")));
return 0;
}
3.34925 times 10 to the power 3
-0. times 10 to the power 0
1.23 times 10 to the power 2
-1.234567890123457 times 10 to the power -1
-inf
nan