4

I'm porting one of my C++ libraries to a somewhat wonky compiler -- it doesn't support stringstreams, or C99 features like snprintf(). I need to format int, float, etc values as char*, and the only options available seem to be 1) use sprintf() 2) hand-roll formatting procedures.

Given this, how do I determine (at either compile- or run-time) how many bytes are required for a formatted floating-point value? My library might be used for fuzz-testing, so it needs to handle even unusual or extreme values.

Alternatively, is there a small (100-200 lines preferred), portable implementation of snprintf() available that I could simply bundle with my library?

Ideally, I would end up with either normal snprintf()-based code, or something like this:

static const size_t FLOAT_BUFFER_SIZE = /* calculate max buffer somehow */;

char *fmt_double(double x)
{
    char *buf = new char[FLOAT_BUFFER_SIZE + 1];
    sprintf(buf, "%f", x);
    return buf;
}

Related questions:

Community
  • 1
  • 1
John Millikin
  • 197,344
  • 39
  • 212
  • 226
  • 1
    Now you've piqued my interest. What compiler wouldn't support _streams_? – sbi Nov 03 '10 at 21:37
  • sbi: tenDRA. It's bizarrely picky about some things (include cstdio? it'll refuse `printf()`, you must use `std::printf()`) and is missing most of the C++ standard library (`std::string`, streams, STL containers, etc). – John Millikin Nov 03 '10 at 21:39
  • that's compliant behaviour - if you want to use `printf()` without the `std` prefix, you'll have to include `stdio.h` instead of `cstdio` (see C++98 D.5 or C++0x draft D.6) – Christoph Nov 03 '10 at 21:57
  • @Christoph: I know, and I'm not complaining about that per se, it's just the only compiler I've ever seen, anywhere, that complies to the spec *that* exactly. – John Millikin Nov 03 '10 at 21:59

2 Answers2

3

Does the compiler support any of ecvt, fcvt or gcvt? They are a bit freakish, and hard to use, but they have their own buffer (ecvt, fcvt) and/or you may get lucky and find the system headers have, as in VC++, a definition of the maximum number of chars gcvt will produce. And you can take it from there.

Failing that, I'd consider the following quite acceptable, along the lines of the code provided. 500 chars is pretty conservative for a double; valid values are roughly 10^-308 to 10^308, so even if the implementation is determined to be annoying by printing out all the digits there should be no overflow.

char *fmt_double(double d) {
    static char buf[500];
    sprintf(buf,"%f",d);
    assert(buf[sizeof buf-1]==0);//if this fails, increase buffer size!
    return strdup(buf);
}

This doesn't exactly provide any amazing guarantees, but it should be pretty safe(tm). I think that's as good as it gets with this sort of approach, unfortunately. But if you're in the habit of regularly running debug builds, you should at least get early warning of any problems...

  • It looks like ecvt() and friends use a static buffer, so they'd make formatting non-threadsafe. I think I'll go with your example code; I'm 100% certain that some implementation, somewhere *will* be as annoying as possible. If somebody complains about the 500-byte buffer, I'll just tell them to use a real compiler. – John Millikin Nov 03 '10 at 22:03
  • Yes that's a good point. I'll admit that I just assumed that the cvt functions have a per-thread buffer, as has been the case for `strtok` and so on on all the implementations I've used. You might find that's the case for the one you're using, too, not that it will fix the cvt annoyingness of course. But if the library doesn't have `snprintf`, who knows what other evil might be lurking in there?! –  Nov 03 '10 at 22:29
  • They do not have per-thread buffers; if they do, that's an implementation being uselessly generous and making itself unnecessarily slow in the process. Of course these functions are legacy junk that shouldn't be used anyway.. – R.. GitHub STOP HELPING ICE Nov 04 '10 at 00:25
1

I think GNU Libiberty is what you want. You can just include the implementation of snprintf.

vasprintf.c - 152 LOC.

  • Browsing through libiberty, it looks like it defines asprintf/vasprintf/etc, but relies on the local system to define snprintf. At least, I don't see any definition of snprintf in the source tree. – John Millikin Nov 03 '10 at 22:07
  • It's in snprintf.c and vsnprintf.c (http://www.koders.com/c/fid306DF70954033C4FB1F24BC1E39A1C249BC2085C.aspx?s=fopen and http://www.koders.com/c/fid8B81E42943C81458A1738DAF313FA6C7D1687376.aspx?s=fopen) –  Nov 03 '10 at 22:19