3

When converting a double to a string like so:

static void strfromdouble(char *buf, size_t n, double d) {
    snprintf(buf, n, "%.14g", d);
}

Which characters could end up in buf[0] after the call to snprintf?

Obviously, it could be one of the following: 0-9, -, . but since we are using floating point math here, there are probably more characters that could end up in buf[0] like I for Inf or N for NaN and maybe even more?

Is the behaviour of snprintf standardized in this situation across different compilers and platforms? If it is, where is the specification, i.e. where can I look up which characters my code would have to be able to deal with in buf[0]?

Andreas
  • 9,245
  • 9
  • 49
  • 97

1 Answers1

1

When converting a double to a string, what can the first string character be?

Easy to generate "0123456789-+ iInN". But other char are possible, depending on how far we want to stretch it.

Obviously, it could be one of the following: 0-9, -, . but since we are using floating point math here, there are probably more characters that could end up in buf[0] like I for Inf or N for NaN and maybe even more?

snprintf(buf, n, "%.14g", d);

When n = 1, expect buf[0] == '\0'

When n = 0, expect buf[0] to be unchanged so any char value is possible there.

With "%.14g" I would not expect 'I' nor 'N', but 'i' and 'n'.
"%.14G" would produce 'I' and 'N'.
A leading '.' does not happen with this format.

With other locales, (research setlocale()), the '-' may be something else. I know of no locale that does that though.

Other formats

With other format "% something [gefaGefa]" strings:

The first character could be a space ' ' or '+'. Like '-', that can vary with locale.

If a leading '.' is possible (I am confident this is not possible with standard formats), others locales may use an alternate such as ','.


With implementation specific formats, any lead character is possible.

With invalid formats, any lead character is possible due to undefined behavior.


#include <stdio.h>

int main(void) {
  char s[1000];
  double inf = strtod("inf", 0);
  double nan = strtod("nan", 0);
  snprintf(s, sizeof s, "%g\n", 0.0);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%g\n", 9.0);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%g\n", -1.0);  printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%+g\n", +1.0); printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%g\n", inf);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%G\n", inf);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%g\n", nan);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%G\n", nan);   printf("%3d %.1s\n", s[0], s);
  snprintf(s, sizeof s, "%5g\n", 1.0);  printf("%3d %.1s\n", s[0], s);
  snprintf(s, 1, "%g\n", 1.0);          printf("%3d %.1s\n", s[0], s);
  s[0] = 'z';
  snprintf(s, 0, "%g\n", 1.0);          printf("%3d %.1s\n", s[0], s);
  return 0;
}

Output

 48 0
 57 9
 45 -
 43 +
105 i
 73 I
110 n
 78 N
 32  
  0 
122 z

Is the behaviour of snprintf standardized in this situation across different compilers and platforms?

Yes. Compliant possibilities include "0123456789-+ iInN". Some compilers may add additional implementation defined ones.


where is the specification (?)

Look for the C spec. Latest is C17

Where do I find the current C or C++ standard documents?


Details:

infinity --> [-]inf or [-]infinity (efga) or uppercase version (EFGA)
nan --> [-]nan or [-]nan(n-char-sequence) or uppercase prefix version

%f --> [−]ddd.ddd,if a decimal-point character appears,at least one digit appears before it.
%F --> like %f
%e --> [−]d.ddd e±dd
%e --> like %e, but with E
%g --> converted in style f or e
%G --> converted in style F or E
%a --> [−]0xh.hhhh p±d
%A --> like %a, but with X,P
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256