Convert floating point to scientific notation in C (nonstandard format)? - c

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

Related

How do I print a floating-point value for later scanning with perfect accuracy?

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.

How to use printf to print out all precision of double float number [duplicate]

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.

Round positive value half-up to 2 decimal places in C

Typically, Rounding to 2 decimal places is very easy with
printf("%.2lf",<variable>);
However, the rounding system will usually rounds to the nearest even. For example,
2.554 -> 2.55
2.555 -> 2.56
2.565 -> 2.56
2.566 -> 2.57
And what I want to achieve is that
2.555 -> 2.56
2.565 -> 2.57
In fact, rounding half-up is doable in C, but for Integer only;
int a = (int)(b+0.5)
So, I'm asking for how to do the same thing as above with 2 decimal places on positive values instead of Integer to achieve what I said earlier for printing.
It is not clear whether you actually want to "round half-up", or rather "round half away from zero", which requires different treatment for negative values.
Single precision binary float is precise to at least 6 decimal places, and 20 for double, so nudging a FP value by DBL_EPSILON (defined in float.h) will cause a round-up to the next 100th by printf( "%.2lf", x ) for n.nn5 values. without affecting the displayed value for values not n.nn5
double x2 = x * (1 + DBL_EPSILON) ; // round half-away from zero
printf( "%.2lf", x2 ) ;
For different rounding behaviours:
double x2 = x * (1 - DBL_EPSILON) ; // round half-toward zero
double x2 = x + DBL_EPSILON ; // round half-up
double x2 = x - DBL_EPSILON ; // round half-down
Following is precise code to round a double to the nearest 0.01 double.
The code functions like x = round(100.0*x)/100.0; except it handles uses manipulations to insure scaling by 100.0 is done exactly without precision loss.
Likely this is more code than OP is interested, but it does work.
It works for the entire double range -DBL_MAX to DBL_MAX. (still should do more unit testing).
It depends on FLT_RADIX == 2, which is common.
#include <float.h>
#include <math.h>
void r100_best(const char *s) {
double x;
sscanf(s, "%lf", &x);
// Break x into whole number and fractional parts.
// Code only needs to round the fractional part.
// This preserves the entire `double` range.
double xi, xf;
xf = modf(x, &xi);
// Multiply the fractional part by N (256).
// Break into whole and fractional parts.
// This provides the needed extended precision.
// N should be >= 100 and a power of 2.
// The multiplication by a power of 2 will not introduce any rounding.
double xfi, xff;
xff = modf(xf * 256, &xfi);
// Multiply both parts by 100.
// *100 incurs 7 more bits of precision of which the preceding code
// insures the 8 LSbit of xfi, xff are zero.
int xfi100, xff100;
xfi100 = (int) (xfi * 100.0);
xff100 = (int) (xff * 100.0); // Cast here will truncate (towards 0)
// sum the 2 parts.
// sum is the exact truncate-toward-0 version of xf*256*100
int sum = xfi100 + xff100;
// add in half N
if (sum < 0)
sum -= 128;
else
sum += 128;
xf = sum / 256;
xf /= 100;
double y = xi + xf;
printf("%6s %25.22f ", "x", x);
printf("%6s %25.22f %.2f\n", "y", y, y);
}
int main(void) {
r100_best("1.105");
r100_best("1.115");
r100_best("1.125");
r100_best("1.135");
r100_best("1.145");
r100_best("1.155");
r100_best("1.165");
return 0;
}
[Edit] OP clarified that only the printed value needs rounding to 2 decimal places.
OP's observation that rounding of numbers "half-way" per a "round to even" or "round away from zero" is misleading. Of 100 "half-way" numbers like 0.005, 0.015, 0.025, ... 0.995, only 4 are typically exactly "half-way": 0.125, 0.375, 0.625, 0.875. This is because floating-point number format use base-2 and numbers like 2.565 cannot be exactly represented.
Instead, sample numbers like 2.565 have as the closest double value of 2.564999999999999947... assuming binary64. Rounding that number to nearest 0.01 should be 2.56 rather than 2.57 as desired by OP.
Thus only numbers ending with 0.125 and 0.625 area exactly half-way and round down rather than up as desired by OP. Suggest to accept that and use:
printf("%.2lf",variable); // This should be sufficient
To get close to OP's goal, numbers could be A) tested against ending with 0.125 or 0.625 or B) increased slightly. The smallest increase would be
#include <math.h>
printf("%.2f", nextafter(x, 2*x));
Another nudge method is found with #Clifford.
[Former answer that rounds a double to the nearest double multiple of 0.01]
Typical floating-point uses formats like binary64 which employs base-2. "Rounding to nearest mathmatical 0.01 and ties away from 0.0" is challenging.
As #Pascal Cuoq mentions, floating point numbers like 2.555 typically are only near 2.555 and have a more precise value like 2.555000000000000159872... which is not half way.
#BLUEPIXY solution below is best and practical.
x = round(100.0*x)/100.0;
"The round functions round their argument to the nearest integer value in floating-point
format, rounding halfway cases away from zero, regardless of the current rounding direction." C11dr §7.12.9.6.
The ((int)(100 * (x + 0.005)) / 100.0) approach has 2 problems: it may round in the wrong direction for negative numbers (OP did not specify) and integers typically have a much smaller range (INT_MIN to INT_MAX) that double.
There are still some cases when like when double x = atof("1.115"); which end up near 1.12 when it really should be 1.11 because 1.115, as a double is really closer to 1.11 and not "half-way".
string x rounded x
1.115 1.1149999999999999911182e+00 1.1200000000000001065814e+00
OP has not specified rounding of negative numbers, assuming y = -f(-x).

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?
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.

Print float/double without trailing zeros? [duplicate]

This question already has answers here:
Avoid trailing zeroes in printf()
(16 answers)
Closed 6 years ago.
There are a few questions related to this, but I haven't seen one that correctly answers this question. I want to print a floating-point number, but I want the number of decimal places to be adaptive. As an example:
0 -> 0
1234 -> 1234
0.1234 -> 0.1234
0.3 -> 0.3
Annoyingly, the %f specifier will only print to a fixed precision, so it will add trailing zeros to all numbers that don't reach that precision. Some have suggested the %g specifier, which works for a set of numbers, but it will switch to scientific notation for some numbers, like this:
printf("%g", 1000000.0); // prints 1e+06
How can I print floating-point numbers without the unnecessary zeros, while still maintaining printf's standard accuracy for numbers that actually have fractional components?
Use snprintf to print to a temporary buffer then remove the trailing '0' characters manually. There is no other way that's both correct and reasonably easy to implement.
The problem is that using IEEE standard 754 representation, floating point values (with a fractional part) can never have "trailing zeros".
Trailing zeros mean that the fractional value can be written as x/10^n for some integers x, n. But the only fractions that can be represented by this standard have the form x/2^n for some integers x, n.
So what you write as 0.1234 is represented using the bytes 0x3D 0xFC 0xB9 0x24. This is:
Sign = 0
Exponent = 01111011 (which means -4)
Significand: 1.11111001011100100100100
The significand means: 1 + 1/2 + 1/4 + 1/8 + 1/16 + 0/32 + 0/64 + 1/128 + 0/256 + 1/512 + 1/1024 + 1/2048 + 0/4096 + 0/8192 + ...
If you perform this calculation, you get 1.974400043487548828125.
So the number is + 1.974400043487548828125 * 2^(-4) = 0.1234000027179718
(I've calculated this using a computer, of course, so it could be off for the same reason...)
As you can see, the computer does not want to decide for you that you want to chop this number after 4 digits (only) and not after 9 digits (0.123400002). The point is that the computer doesn't see this number as 0.1234 with an infinite number of trailing zeros.
So I don't think there's a better way than R.'s.
Try:
printf("%.20g\n", 1000000.0); // = 1000000
This will switch to scientific notation after 20 significant digits (default is after 6 digits for "%g"):
printf("%.20g\n", 1e+19); // = 10000000000000000000
printf("%.20g\n", 1e+20); // = 1e+20
But be careful with double precision:
printf("%.20g\n", 0.12345); // = 0.12345000000000000417
printf("%.15g\n", 0.12345); // = 0.12345
I wrote a small function to do this myself using the method already mentioned, here it is with a tester. I can't guarantee it's bug free but I think it's fine.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *convert(char *s, double x);
int main()
{
char str[3][20];
printf("%s\n%s\n%s\n", convert(str[0], 0),
convert (str[1], 2823.28920000),
convert (str[2], 4.000342300));
}
char *convert(char *s, double x)
{
char *buf = malloc(100);
char *p;
int ch;
sprintf(buf, "%.10f", x);
p = buf + strlen(buf) - 1;
while (*p == '0' && *p-- != '.');
*(p+1) = '\0';
if (*p == '.') *p = '\0';
strcpy(s, buf);
free (buf);
return s;
}
Output:
0
2823.2892
4.0003423
You can print like this:
printf("%.0f", 1000000.0);
For a more detailed answer, look here.

Resources