conversion between string and numerical value

Discussion in 'C++' started by Aman JIANG, Sep 19, 2007.

  1. Aman JIANG

    Aman JIANG Guest

    hi

    i need a fast way to do lots of conversion that between
    string and numerical value(integer, float, double...), and
    boost::lexical_cast is useless, because it runs for a long time,
    (about 60 times slower than corresponding C functions)
    it's too expensive for my program.

    is there any way([template] library?) to do this fast and safely,
    please ?
    (old C-style functions is unbecoming)
     
    Aman JIANG, Sep 19, 2007
    #1
    1. Advertisements

  2. So, use the C functions.
    I don't know of any, but should be relatively easy for you to roll
    your own, no?
    Why?

    V
     
    Victor Bazarov, Sep 19, 2007
    #2
    1. Advertisements

  3. How about:

    #v+
    std::string toString(long value) {
    char buffer[100];
    sprintf(buffer, "%ld", value);
    return std::string(buffer);
    }

    std::string toString(unsigned long value) {
    char buffer[100];
    sprintf(buffer, "%lu", value);
    return std::string(buffer);
    }

    long atol(std::string str) { return atol(str.c_str()); }
    double atof(std::string str) { return atof(str.c_str()); }
    #v-

    It's C++ all right.
     
    Michal Nazarewicz, Sep 19, 2007
    #3
  4. Aman JIANG

    Aman JIANG Guest

    because its style, it doesn't support wchar_t, and template and so
    on...
     
    Aman JIANG, Sep 20, 2007
    #4
  5. Aman JIANG

    Aman JIANG Guest

    thank you :)
    that "100" is a magic number.
    and this cannot support wchar_t, too :(
     
    Aman JIANG, Sep 20, 2007
    #5
  6. One more:

    std::string toString(double value) {
    char buffer[100];
    sprintf(buffer, "%f", value);
    return std::string(buffer);
    }
    Yep, its a number long enough to hold a string representation of
    a (unsigned) long value.
    Uhm? You can rewrite it to support it. Just replace char by wchar_t and
    sprintf with swprintf but I'm not sure if you really need it since the
    value is converted into std::string anyways.
     
    Michal Nazarewicz, Sep 20, 2007
    #6
  7. Michal Nazarewicz a écrit :
    You can use numeric_limits<>::digits10 to fit the size to you type.

    // add one for \0 and for additional digit
    // add one more for sign with signed
    char buffer [std::numeric_limits<unsigned long>::digits10 + 2];

    In fact, with traits, it should be possible to write a template.

    Michael
     
    Michael DOUBEZ, Sep 20, 2007
    #7
  8. Aman JIANG

    James Kanze Guest

    On some machines. There's no guarantee (although it's likely to
    be sufficient in the near future).

    It's not necessarily large enough to hold a "%f" conversion of a
    float, and it's not large enough to hold a "%f" conversion of a
    double on the machines I usually work on.

    I might add that there is no guarantee that sprintf is faster
    than boost::lexical_cast, although it's likely *if*
    boost::lexical_cast doesn't have partial specializations for
    std::string (since in the general case, boost::lexical_cast must
    do two conversions, and not just one).

    More generally, you might try:

    std::string
    toString( double value )
    {
    std::eek:stringstream s ;
    s << value ;
    return s.str() ;
    }

    or if that's too slow:

    std::string
    toString( double value )
    {
    char buffer[ 100 ] ;
    std::eek:strstream s ;
    s << value << std::ends ;
    assert( s ) ;
    return buffer ;
    }

    (This will at least cause an assertion failure, rather than
    undefined behavior, if the converted string requires more than
    99 characters. You can, of course, replace the assertion with
    any other error handling you wish.)

    If all else fails, you can try snprintf, which is not standard
    C++, but is standard C99, and probably available with your
    implementation (and allows for error checking as well).

    Also, you probably want to use the "%g" format (the default for
    iostream), unless you know that the input is in a very limited
    range.
     
    James Kanze, Sep 20, 2007
    #8
  9. Aman JIANG

    James Kanze Guest

    The original poster also had a version for double.
    Possible, but rather tricky. Don't forget that the format
    specifier would have to depend on the type. You'd end up having
    to explicitly specialize the traits for each type, with as much
    work as if you'd just written a separate function for each type.

    Just another reason why nobody in their right mind would want to
    use the printf family of functions.
     
    James Kanze, Sep 20, 2007
    #9
  10. There are plenty of people (including myself) who prefer C stdio to C++
    streams.
     
    Michal Nazarewicz, Sep 20, 2007
    #10
  11. Aman JIANG

    anon Guest

    Looks like something is missing here.
    1) Where is buffer used?
    2) what is std::ends?
     
    anon, Sep 20, 2007
    #11
  12. Aman JIANG

    BobR Guest

    In the 'return'! <G>

    std::eek:strstream s( buffer, 100 ) ; // ??
    Manipulator: ends
    Write `\0' (the string terminator character).
     
    BobR, Sep 20, 2007
    #12
  13. Aman JIANG

    Aman JIANG Guest

    Thank you :)

    I think use 'stringstream' to do this work is still expensive,
    although it must be faster than boost::lexical_cast. We know that
    'strstream' was "deprecated" in C++98.

    Is there another way to do this, no stream, and no old C-style
    functions, please ?
    (sorry my english is not well)
     
    Aman JIANG, Sep 21, 2007
    #13
  14. Aman JIANG

    James Kanze Guest

    Yep. That's what I meant. I've gotten so used to
    std::eek:stringstream that I forgot. Even though that was the
    whole point of the thing: use an on stack buffer, rather than a
    dynamically allocated one.
     
    James Kanze, Sep 21, 2007
    #14
  15. Aman JIANG

    James Kanze Guest

    [...]
    In what way? It might use dynamic allocation, but then, if you
    use std::string, you use dynamic allocation. How much depends;
    if your implementation of std::string uses the short string
    optimization, and the strings you generate fit into the short
    string, it's possible that there is no dynamic allocation
    anywhere.

    In general, the relative performance will depend on the
    implementation. I've used implementations where
    std::eek:stringstream was about the same speed as sprintf, maybe
    even faster for some things, and I've used implementations where
    it was almost an order of magnitude slower.
    So. It's still there, and will be for a long time.
    You can write the conversion routines yourself. Since you don't
    need all of the formatting options, you should be able to come
    up with something considerably faster than any of the standard
    functions. For integral types, it's actually not very
    difficult. For floating point types, on the other hand...
    correctly converting floating point is far from trivial.
     
    James Kanze, Sep 21, 2007
    #15
  16. Aman JIANG

    Kai-Uwe Bux Guest

    I do not understand this version. Where is the magic that modifies the
    variable buffer? and how are buffer and the ostrstream s related? It looks
    as though you return an uninitialized array of char.


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Sep 21, 2007
    #16
  17. Aman JIANG

    Aman JIANG Guest

    'dynamic allocation' was not very slow, but re-copy was.
    if there is no re-copy, string will be cheap.
    i have interest at the implementation what you mentioned.
    what's the implementation's name, please ?
    I have no idea to do this. As you said, integral is easy, but
    floating-point is a hot potato.
     
    Aman JIANG, Sep 22, 2007
    #17
  18. Aman JIANG

    James Kanze Guest

    You'll get the re-copy just about anyway on output. Maybe
    something like:

    std::string
    toString( double value )
    {
    std::string result( ???, '\0' ) ;
    std::eek:strstream s( const_cast< char* >( &result[ 0 ] ) ,
    result.size() - 1 ) ;
    s << value ;
    result.resize( strlen( result.c_str() ) ) ;
    return result ;
    }

    Not very pretty, though. (Technically, it's also undefined
    behavior according to the current version of the standard. It
    does work with all existing implementations, however, and will
    be guaranteed in the next release of the standard.)
    I'm not 100% sure now, but I think it was an older version of
    the library included with g++. The one that was incredibly slow
    was the Rogue Wave implementation that comes with Sun CC; file
    IO was slow enough to render the library unusable in certain
    applications (although we'd been using it before, with the
    pre-standard version of the library that came with Sun CC 4.2,
    without problems).

    [...]
    The problem with floating-point is the usual problem with
    floating point. Writing a conversion routine is easy. Writing
    one which is correct for all input values, is much, much harder.

    There are texts available on the Web which explain it (by Guy
    Steele, I think), but quite frankly, I would only go that route
    as a last resort, if all else failed.

    Note that if you don't have a problem with your code ending up
    under the GPL, you could probably use the core of whatever is
    present in the GNU libc or the g++ library, adopting it to
    whatever you need. Note that this use does NOT come under the
    additional liberties granted by the LGPL or by the g++ library
    itself, so your code would (at least formally) be
    "contaminated".
     
    James Kanze, Sep 23, 2007
    #18
  19. Aman JIANG

    Kai-Uwe Bux Guest

    I think it would be very hard to beat the sprintf family. Those functions
    tend to be highly optimized and crucial parts are coded in assembler. I
    would just wrap it. Note: according to the current standard, writing into
    the string directly is undefined behavior. However, it is proposed for the
    next standard an very likely to work with current std::string
    implementations.


    std::string to_string ( double d ) {
    // TRICKY: [multithreading]
    /*
    The use of this static counter might
    cause problems in multithreaded code.
    */
    static unsigned long length_provided = 1;
    std::string result;
    result.resize( length_provided );
    // TRICKY: [we assume that std::string is contiguous]
    /*
    This is proposed for the next version of the standard.
    Check your library.
    */
    int length_needed =
    snprintf( &result[0], length_provided, "%f", d );
    assert( length_needed >= 0 );
    while ( length_needed > length_provided ) {
    length_provided = length_needed;
    result.resize( length_provided );
    length_needed =
    snprintf( &result[0], length_provided, "%f", d );
    assert( length_needed >= 0 );
    }
    return ( result );
    }


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Sep 23, 2007
    #19
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.