1

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 (!).

10
  • 1
    "The problem is that this does not work with %g instead of %f." Not good enough. Be precise. How does it not work? Commented Jan 4, 2017 at 15:20
  • Although OP of another post did not care for the answer , 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. Commented Jan 4, 2017 at 15:41
  • 1
    Why do you want output to be -123.123456789123456 rather than the more precise -123.123456789123457? (check last digit) Commented Jan 4, 2017 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"? Commented Jan 4, 2017 at 15:49
  • 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? Commented Jan 4, 2017 at 15:56

1 Answer 1

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);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.