snprintf can be used to determine the size, with a 0 argument as the size:
double x = 42;
const char fmt[] = "%f";
int size = snprintf(0, 0, fmt, x);
if(size < 0) { /* handle error */ }
char buf[size+1];
sprintf(buf, fmt, x);
The C standard requires snprintf with a 0 size argument to work. Note, though, that some older implementations don't conform (e.g. Glibc until version 2.0.6 returns -1 if the output was truncated instead of the number of characters that would have been written, according to the man page).
The error checking above may be useless, the only possible error which can occur is an encoding error.
You shouldn't change the locale between the snprintf and the sprintf call, the locale may affect the size. The sprintf can be replaced by a snprintf call again if intervening locale changes are a concern, but then you probably want to allocate the memory via malloc, and then realloc while necessary. [Thanks, Chux!]
Under Glibc, there is also asprintf which allocates the buffer itself. Don't forget to free it afterwards. [Thanks to Abligh!]