Cross platform support for sprintf '-flag format

A single UNIX version 2 specification defines the behavior of sprintf format ' -flag as:

The integer part of the result of the decimal conversion ( %i , %d , %u , %f , %g or %g ) will be formatted with thousandths of the grouping [ 1 ]

I cannot find the ' -flag format in the c or C ++ specification. g ++ even warns :

ISO C ++ 11 does not support the ' printf ' flag

The flag is not recognized even for warning in Visual C; printf("%'d", foo) outputs :

'd

I would like to write C-compliant code that uses the ' -flag format behavior. So the answer I was looking for has one of the following meanings:

  • C-standard format specification ' -flag
  • Cross-platform gcc ' -flag extrapolation compatibility
  • Demonstration of the impossibility of extrapolating a cross platform
+1
c cross-platform printf number-formatting sus
Jun 13 '17 at 14:03
source share
1 answer

The C standard does not provide formatting directly, but provides the ability to get ... a specification of what formatting should be, depending on the locale. So, you need to get the locale specification of the correct formatting, and then use it to format your data (but even then it is somewhat nontrivial). For example, here is the version for formatting long data:

 #include <stdlib.h> #include <locale.h> #include <string.h> #include <limits.h> static int next_group(char const **grouping) { if ((*grouping)[1] == CHAR_MAX) return 0; if ((*grouping)[1] != '\0') ++*grouping; return **grouping; } size_t commafmt(char *buf, /* Buffer for formatted string */ int bufsize, /* Size of buffer */ long N) /* Number to convert */ { int i; int len = 1; int posn = 1; int sign = 1; char *ptr = buf + bufsize - 1; struct lconv *fmt_info = localeconv(); char const *tsep = fmt_info->thousands_sep; char const *group = fmt_info->grouping; // char const *neg = fmt_info->negative_sign; size_t sep_len = strlen(tsep); size_t group_len = strlen(group); // size_t neg_len = strlen(neg); int places = (int)*group; if (bufsize < 2) { ABORT: *buf = '\0'; return 0; } *ptr-- = '\0'; --bufsize; if (N < 0L) { sign = -1; N = -N; } for ( ; len <= bufsize; ++len, ++posn) { *ptr-- = (char)((N % 10L) + '0'); if (0L == (N /= 10L)) break; if (places && (0 == (posn % places))) { places = next_group(&group); for (int i=sep_len; i>0; i--) { *ptr-- = tsep[i-1]; if (++len >= bufsize) goto ABORT; } } if (len >= bufsize) goto ABORT; } if (sign < 0) { if (len >= bufsize) goto ABORT; *ptr-- = '-'; ++len; } memmove(buf, ++ptr, len + 1); return (size_t)len; } #ifdef TEST #include <stdio.h> #define elements(x) (sizeof(x)/sizeof(x[0])) void show(long i) { char buffer[32]; commafmt(buffer, sizeof(buffer), i); printf("%s\n", buffer); commafmt(buffer, sizeof(buffer), -i); printf("%s\n", buffer); } int main() { long inputs[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678 }; for (int i=0; i<elements(inputs); i++) { setlocale(LC_ALL, ""); show(inputs[i]); } return 0; } #endif 

This has a bug (but I find it pretty minor). On two additional hardware, he will not correctly convert the most negative number, since he is trying to convert a negative number into its equivalent positive number using N = -N; . In two additions, a maximally negative number does not have a corresponding positive number unless you advance it to a larger type. One way around this is to promote the number of the corresponding unsigned type (but it is somewhat nontrivial).

The implementation for other integer types is pretty trivial. For floating point types, a bit more work. Converting floating-point types correctly (even without formatting) is a lot of work, so I would at least think of using something like sprintf to convert and then paste the formatting into the string that was created.

+2
Jun 14 '17 at 14:26
source share



All Articles