0

I need to print doubles in C, with a width of 20, but using the maximum precision that can be printed in the 20 chars

For example:

-123.123456789123456789 

should be printed:

-123.1234567891234568

Up to now I have the following code that works, but is ugly and has a problem:

double num = .......;
int pr = 0;
char temp[32] = { 0 };
char *point = NULL;
sprintf(temp, "%.20f", num);
point = strchr(temp, '.');
if (point) {
    pr = 20 - (1 + point - temp);
}
printf("%20.*f", pr, num);

The problem is that this does not work with %g instead of %f, it prints more than 20 characters sometimes, even though no exponential part exists. The problem arises when a number like 0.000123456789123456789 is given. The extra zeroes after the decimal do not seem to count (!).

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 1
    *"The problem is that this does not work with %g instead of %f."* Not good enough. Be precise. How does it not work? – user694733 Jan 04 '17 at 15:20
  • Although OP of another post did not care for the [answer](http://stackoverflow.com/a/41402788/2410359) , that does what this OP wants - I think. The issue is that corner cases exist that make using a pre-calculated format not optimal. The linked answer tries various `"%.*e"` solutions. – chux - Reinstate Monica Jan 04 '17 at 15:41
  • 1
    Why do you want output to be `-123.123456789123456` rather than the more precise `-123.123456789123457`? (check last digit) – chux - Reinstate Monica Jan 04 '17 at 15:44
  • 1) "print doubles in C, with a width of 20" --> how do you want to print a `double` like `DBL_MAX` which may need 300+ digits? Or do you want to jump to exponential notation? 2) Should positive +123.123456789123456789 print with 1 more decimal place as `"123.1234567891234567"`? – chux - Reinstate Monica Jan 04 '17 at 15:49
  • What about `double` like `DBL_MIN` which may have a value near 1e-300? – chux - Reinstate Monica Jan 04 '17 at 15:51
  • 1) That's why I want to use %g, so as it jumps to exponential. – Pavlos Katsonis Jan 04 '17 at 15:52
  • 2) my mistake: it should be 123.1234567891234568. That is 16 digits after the decimal – Pavlos Katsonis Jan 04 '17 at 15:55
  • 1
    `"%g"` does not always meet "using the maximum precision that can be printed" requirement, but jumps to exponential via a nearly similar criteria. Which is more important: maximum precision, ease of coding, correctness across all `double`? – chux - Reinstate Monica Jan 04 '17 at 15:56
  • Correctness is the most important. There is no problem if a digit or two get lost under some circumstances. – Pavlos Katsonis Jan 04 '17 at 15:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132314/discussion-between-chux-and-pavlos-katsonis). – chux - Reinstate Monica Jan 04 '17 at 16:01

1 Answers1

1

Following some hints from chux and this answer, I implemented the following solution that seems to work. I no longer try to pre-calculate the correct precision to use, but print with more than enough precision and later I crop the superfluous digits.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void printDouble(double val, int width)
{
    char *temp = malloc((width + 32) * sizeof(char));
    int len = 0;
    sprintf(temp, "%*.*g", width, width, val);
    len =strlen(temp);
    if (len > width) {
        char *dropped = temp + width;
        char *last = dropped - 1;
        char *expon = strchr(temp, 'E');
        if (!expon)
            expon = strchr(temp, 'e');
        if (expon) {
            last = temp + width - 1 - strlen(expon);
            dropped = last + 1 < expon ? last + 1 : NULL;
        }
        if (strchr(temp, '.')) {
            /* Round */
            if (dropped && *dropped >= '5' && *dropped <= '9') {
                while (*last == '9')
                    last--;
                (*last)++;
            }
            /* Remove trailing zeroes */
            while (*last == '0')
                last--;
        }
        if (expon) {
            while (*expon != '\0')
                *(++last) = *(expon++);
        }
        *(++last) = '\0';
    }
    printf("%*s\n", width, temp);
    free(temp);
}
Community
  • 1
  • 1