You could roll your own printing code, something like this:
1. Test for NaN with x != x
2. Test for infinity by comparing with FLT_MAX (in <cfloat>)
3. Split into integer and fractional parts with modf (<cmath>)
4. Repeatedly div-mod the integer part to extract the digits
left of the decimal point, and repeatedly multiply and modf
the fractional part to get the digits to the right. It will
probably be faster to work in base 10^9 rather than 10.
a) The numerical analysis involved to make sure that all digits are correct
is far from trivial.
b) Performancewise, it will be close to impossible to beat the sprintf
family. Those library functions are heavily optimized and crucial parts are
probably coded in assembler.
I would suggest to wrap snprintf within a nice safe layer:
template < typename A >
bool strprintf ( std::string & buffer, char const * formant, A a );
template < typename A, typename B >
bool strprintf ( std::string & buffer, char const * formant, A a, B b );
...
When passing a buffer whose capacity is high enough, the function should not
need dynamic allocation. Moreover, using the contiguity requirement for
std::string from the next revision of the standard, already implemented by
most (all?) STL-implementations, one does not even need to use an internal
buffer. Something like:
template < typename A >
bool strprintf ( std::string & buffer, char const * format, A a ) {
while ( true ) {
buffer.append( 1, '\0' );
int length_needed =
snprintf( &buffer[0], buffer.size(), format, a );
if ( length_needed < 0 ) {
return ( false );
}
if ( length_needed > buffer.size() ) {
buffer.resize( length_needed );
} else {
buffer.resize( length_needed );
return ( true );
}
}
}
Assuming that std::string actually holds a buffer that always keeps a 0-char
at the end, one could even do:
template < typename A >
bool strprintf ( std::string & buffer, char const * format ) {
while ( true ) {
int old_length = buffer.size();
int length_needed =
snprintf( &buffer[0], old_length + 1, format, a );
if ( length_needed < 0 ) {
return ( false );
}
buffer.resize( length_needed );
if ( length_needed <= old_length ) {
return ( true );
}
}
}
However, I think that will have undefined implementation even according to
the current working draft.
In order to make double calls to snprintf as rare as possible, one could
also resize the buffer to its current capacity (beware, this also has
undefined behavior but I expect it to be portable).
template < typename A >
bool strprintf ( std::string & buffer, char const * format ) {
while ( true ) {
buffer.resize( buffer.capacity() );
int old_length = buffer.size();
int length_needed =
snprintf( &buffer[0], old_length + 1, format, a );
if ( length_needed < 0 ) {
return ( false );
}
buffer.resize( length_needed );
if ( length_needed <= old_length ) {
return ( true );
}
}
}
Anyway, to print millions of doubles, I would try a loop like this:
std::string buffer;
while ( whatever ) {
strprintf( buffer, "%f", my_function() );
std::cout << buffer << '\n';
}
and measure which one works best (according to my measurements, the last
version is slightly faster than the other two).
Best
Kai-Uwe Bux