2

I'm trying to print floating point numbers 1.2345678, 0.1234567, 123.45678 using printf in such a way that only the first n characters are printed. I want these number to line up. The %g format specifier in printf("%*g", n, var) does this but the specifier is not treating the 0 in 0.1234567 as a significant figure. This causes the alignment of 0.1234567 to go off wrt to the other two figures.

What's the best way to align the numbers in the formats given. Either by treating 0 as significant with %g or using some other method?

jeff carey
  • 2,313
  • 3
  • 13
  • 17

3 Answers3

0

By definition leading zeros are not significant figures. If you want the first N digits to be printed, including leading zeros, convert first the numbers to strings and use printf("%.*s", n, var_as_string).

This way, the first n characters are indeed printed, as you asked. Of course the decimal mark ., as any other character, is now significant, contrary to %g.

xhienne
  • 5,738
  • 1
  • 15
  • 34
  • 1
    True that. Only (major) flaw is that the figures aren't automatically rounded up or down. –  Jan 02 '17 at 14:08
  • @OS2 If you use `s(n)printf` to convert your numbers to strings, you will have the same rounding as `printf` – xhienne Jan 02 '17 at 15:15
  • s(n)printf copies data to a buffer. But then you are printing a truncated subset of that string using printf which doesn't take the rounding into account. Am I missing something? –  Jan 03 '17 at 16:16
  • @OS2 No, that was me who was missing something. Now I get it, thanks. – xhienne Jan 03 '17 at 16:20
0

A brute force method tries various precisions of %.*g until success.

The major obstacle to calculating the width of the print out before calling printf() is that corner cases exist like 0.9995. The subtle effects of rounding make it that it is simpler to call sprintf() a subsequent time. Additional code could be uses to make a better initial guess rather than starting at prec = n - 1

int g_print(double x, int n) {
  int width;
  char buffer[n + 1];
  for (int prec = n - 1; prec >= 0; prec--) {
    width = snprintf(buffer, sizeof buffer, "%.*g", prec, x);
    // printf("  n:%2d p:%2d w:%2d <%s>\n", n, prec, width, buffer);
    if (width > 0 && (unsigned) width < sizeof buffer) {
      return printf("%2d <%s>\n", n, buffer);
    }
  }
  // Try with %f
  width = snprintf(buffer, sizeof buffer, "%.0f", x);
  if (width > 0 && (unsigned) width < sizeof buffer) {
    return printf("%2d <%s>\n", n, buffer);
  }
  printf("%2d Fail %e\n", n, x);
  return 0; // fail
}

void g_print_test(double x) {
  // Need at least  width 7 for all values
  for (int n = 8; n >= 1; n--) {
    if (g_print(x, n) == 0)
      break;
  }
}


int main() {
  g_print_test(1.2345678);
  g_print_test(0.1234567);
  g_print_test(123.45678);
  g_print_test(DBL_MAX);
  g_print_test(-DBL_MAX);
  g_print_test(-DBL_MIN);
  return 0;
}

Output

 n  text...
 8 <1.234568>
 7 <1.23457>
 6 <1.2346>
 5 <1.235>
 4 <1.23>
 3 <1.2>
 2 <1>
 1 <1>
 8 <0.123457>
 7 <0.12346>
 6 <0.1235>
 5 <0.123>
 4 <0.12>
 3 <0.1>
 2 <0>
 1 <0>
 8 <123.4568>
 7 <123.457>
 6 <123.46>
 5 <123.5>
 4 <123>
 3 <123>
 2 Fail 1.234568e+02
 8 <1.8e+308>
 7 <2e+308>
 6 <2e+308>
 5 Fail 1.797693e+308
 8 <-2e+308>
 7 <-2e+308>
 6 Fail -1.797693e+308
 8 <-2e-308>
 7 <-2e-308>
 6 <-0>
 5 <-0>
 4 <-0>
 3 <-0>
 2 <-0>
 1 Fail -2.225074e-308
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thanks for the effort in describing this. But too complicated as a way to represent output. Not interested. –  Jan 02 '17 at 14:10
0

I want these number to line up. The %g format specifier in printf("%*g", n, var) does this

No, it doesn't - with %*g you're only specifying a minimum field width… In no case does a nonexistent or small field width cause truncation of a field; if the result of a conversion is wider than the field width, the field is expanded to contain the conversion result. [from man 3 printf]

But perhaps you meant %.*g, specifying the precision …, the maximum number of significant digits, which behaves according to your problem description.

What's the best way to align the numbers in the formats given. Either by treating 0 as significant with %g or using some other method?

What's the best way depends on the value range of the numbers. If we have just positive numbers similar to those you posted, we could simply reduce the precision by one for a leading zero, e. g.:

    printf("%.*g", n-(var<1), var);
Armali
  • 18,255
  • 14
  • 57
  • 171