anfängerprobleme mit sprintf



  • Michael E. schrieb:

    Sorry, buffer ist ja ein std::array und kein std::string. Die Argumentation bleibt aber dieselbe: buffer.end() ist ein array::iterator, &(*iter) ein Pointer, beide müssen denselben Typ haben.

    boost::array implementiert den Iterator als Pointer, da dachte ich mal die std Implementierung macht das genauso. Was sagt der Standard dazu? Implementation defined?

    Im Zweifelsfall muss man es halt korrekt machen, mit der Lösung von Camper, also Base() auf den Reverse Iterator aufrufen.


  • Mod

    Habe zum Spaß auch eine Version geschrieben

    #include <iostream>
    #include <algorithm>
    #include <array>
    #include <limits>
    #include <sstream>
    #include <iomanip>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    
    std::string to_hex_string_swordfish( unsigned value ) {
        static std::ostringstream ss;
        ss.clear( );
        ss.str( "" );
        ss << std::hex << value;
        return ss.str( );
    }
    
    std::string to_hex_string_hacker( unsigned value)
    {
        std::ostringstream stream;
        stream << std::hex << value;
        return stream.str();
    }
    
    std::string to_hex_string_ethon(unsigned value)
    {
        char buffer[sizeof(value) * 2 + 1];
        sprintf(buffer,"%x", value);
        return std::string(buffer);
    }
    
    std::string to_hex_string_ethon2(unsigned value)
    {
        typedef std::string string_type;
        typedef char CharT;
        string_type result(sizeof(value) * 2, 0);
        typename string_type::reverse_iterator iter = result.rbegin();
        while(value > 16)
        {
            unsigned cur = value % 16;
            *iter = cur < 10 ? CharT('0') + cur : CharT('A') + cur - 10;
            ++iter;
            value /= 16;
        }
    
        *iter = value < 10 ? CharT('0') + value : CharT('A') + value - 10;
        return result;
    }
    
    std::string to_hex_string_ethon3(unsigned value)
    {
        typedef char CharT;
        const unsigned Base = 16;
        CharT const lookup[] =
            {   CharT('0'), CharT('1'), CharT('2'), CharT('3'), CharT('4'),
                CharT('5'), CharT('6'), CharT('7'), CharT('8'), CharT('9'),
                CharT('A'), CharT('B'), CharT('C'), CharT('D'), CharT('E'),
                CharT('F') };
    
        typedef std::basic_string<CharT> string_type;
        string_type result(sizeof(value) * 2, 0);
        typename string_type::reverse_iterator iter = result.rbegin();
        while(value > Base)
        {
            *iter = lookup[value % Base];
            ++iter;
            value /= Base;
        }
    
        *iter = lookup[value];
        return result;
    }
    std::string to_hex_string_ethon4(unsigned value)
    {
        typedef char CharT;
        const unsigned Base = 16;
    
        CharT const lookup[] =
        {
            CharT('0'), CharT('1'), CharT('2'), CharT('3'),
            CharT('4'), CharT('5'), CharT('6'), CharT('7'),
            CharT('8'), CharT('9'), CharT('A'), CharT('B'),
            CharT('C'), CharT('D'), CharT('E'), CharT('F')
        };
    
        std::array<CharT, std::numeric_limits<unsigned>::digits> buffer;
        auto iter = buffer.rbegin();
        while(value > Base)
        {
            *iter = lookup[value % Base];
            ++iter;
            value /= Base;
        }
    
        *iter = lookup[value];
        return std::basic_string<CharT>(iter.base(), buffer.end());
    }
    std::string to_hex_string_incognito(unsigned value)
    {
        static std::string hex;
        hex.clear();
        do
        {
            if( value%16 < 10)
                hex += value%16 + '0';
            else if(value%16 > 9 && value%16 < 17)
                hex += value%16 + 55;
        } while(value /= 16);
        std::reverse(hex.begin(), hex.end());
        return hex;
    }
    template <typename T>
    struct identity
    {
    
        typedef T type;
    };
    template <std::size_t... i> struct index_list
        : identity<index_list<i...>> {};
    
    template <typename T> struct append_index_list
        : append_index_list<typename T::type> {};
    template <std::size_t... i> struct append_index_list<index_list<i...>>
        : identity<index_list<(i+1)..., 0>> {};
    
    template <std::size_t N> struct make_index_list
        : append_index_list<make_index_list<N-1>> {};
    template <> struct make_index_list<0>
        : identity<index_list<>> {};
    
    template <typename T, std::size_t... i>
    std::string to_hex_string_camper_(T value, index_list<i...>)
    {
        const char lut[] = "0123456789abcdef";
        return std::string{ lut[(value>>(4*i))%16]... };
    }
    std::string to_hex_string_camper(unsigned value)
    {
        return to_hex_string_camper_(value,make_index_list<(std::numeric_limits<unsigned>::digits+3)/4>::type());
    }
    
    #define MEASSURE( name )                                            \
        {                                                               \
            std::clock_t start = std::clock();                          \
            for( size_t i = 0; i < 2000000; ++i )                       \
                result = to_hex_string_##name( rand() );             \
            std::clock_t end = std::clock();                            \
            double elapsed = ( end - start ) / frequency;                      \
            std::cout << #name << '\t' << to_hex_string_##name(0x12345678) << '\t' << elapsed << std::endl; \
        }
    int main()
    {
        std::cout << RAND_MAX << std::endl;
        std::srand( static_cast< unsigned >( std::time( 0 ) ) );
        std::string result;
        double frequency = CLOCKS_PER_SEC;
        MEASSURE( swordfish );
        MEASSURE( hacker );
        MEASSURE( ethon );
        MEASSURE( ethon2 );
        MEASSURE( ethon3 );
        MEASSURE( ethon4 );
        MEASSURE( incognito );
        MEASSURE( camper );
    }
    

    Mit -march=native (atom,64bit) und -O3 erhalte ich

    swordfish	12345678	2.14
    hacker	12345678	9.96
    ethon	12345678	1.95
    ethon2	12345678	0.98
    ethon3	12345678	0.97
    ethon4	2345678	0.81
    incognito	12345678	1.45
    camper	12345678	0.8
    

    mit gcc-4.7, mit gcc-4.6.2 dagegen

    swordfish	12345678	2.81
    hacker	12345678	13.38
    ethon	12345678	2.29
    ethon2	12345678	1.28
    ethon3	12345678	1.27
    ethon4	2345678	1.01
    incognito	12345678	1.73
    camper	12345678	1.13
    

    Der große Unterschied ist doch etwas überraschend.
    gcc-4.5.3

    swordfish	12345678	2.74
    hacker	12345678	12.91
    ethon	12345678	2.2
    ethon2	12345678	1.24
    ethon3	12345678	1.2
    ethon4	2345678	1.02
    incognito	12345678	1.68
    camper	12345678	1.1
    


  • Ihr könnt mich gleich rausnehmen, meine Version war nicht großartig überdacht 😃



  • gut, ich wollte hier vorher nur zeigen, dass es performancetechnischer quatsch ist, den stringstream bei jedem call zu bauen und wieder zu zerstoeren - was campers benchmark NOCH deutlicher zeigt. Besser als mit einem lookup table wirds wohl kaum gehen, revidierte fassung zwecks performanceschwanzvergleich folgt...

    // edit:

    std::string to_hex_string_swordfish_2( unsigned value ) {
    	const char lookup[ ] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    	char buffer[ 16 ];
    	size_t i = 0;
    
    	do {
    		buffer[ i++ ] = lookup[ value % 16 ];
    	} while( value /= 16 );
    
    	return std::string( buffer, i );
    }
    

    Meine letzte Messung:

    Swordfish:   596.335
    Swordfish 2: 38.8694
    Hacker:      1009.47
    Ethon:       172.068
    Ethon 2:     33.1679
    Ethon 3:     50.1497
    Incocnito:   69.6656
    


  • Bis darauf dass deins falsche Ergebnisse produziert. 😉

    int main()
    {
    	std::cout << to_hex_string_swordfish_2(0xDEADBEEF) << std::endl;
    }
    

    Ergibt

    FEEBDAED

    So ... damit das Ganze mal sinnvoll wird, Boost.Spirit ist doch bei soetwas immer der King. Jemand da, der das mit Spirit drauf hat?



  • std::string to_hex_string_karma(unsigned p_unsigned)
    {
    	using boost::spirit::karma::generate;
    	using boost::spirit::karma::hex;
    	std::string result;
    	std::back_insert_iterator<std::string> it(result);
    	generate(it, hex(p_unsigned));
    
    	return result;
    }
    

    Kompiliert mit MSVC++ 10.0

    edit nochmal, hier die vergleichswerte. geht das noch besser in spirit?

    32767
    swordfish       12345678        3.356
    hacker          12345678        9.658
    ethon           12345678        0.546
    ethon2          12345678        0.109
    ethon3          12345678        0.078  <- king
    ethon4          2345678         0.172
    incognito       12345678        0.218
    karma           12345678        0.265
    


  • Ethon schrieb:

    Bis darauf dass deins falsche Ergebnisse produziert. 😉

    ok, dann eben rückwärts (wenn Du nicht rückwärts lesen magst :p )

    std::string to_hex_string( unsigned value ) {
    	const char lookup[ ] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    	char buffer[ 16 ];
    	size_t i = 15;
    
    	do buffer[ --i ] = lookup[ value % 16 ];
    	while( value /= 16 );
    
    	return std::string( buffer + i, 15 - i );
    }
    


  • Warum macht ihr's euch immer so kompliziert mit dem lookup, wie wärs mit:

    "0123456789ABCDEF"[value % 16]
    


  • 314159265358979 schrieb:

    Warum macht ihr's euch immer so kompliziert mit dem lookup, wie wärs mit:

    "0123456789ABCDEF"[value % 16]
    

    Sieht verdächtig einfach aus...



  • Nana, des passt schon. Ich find's halt ned schön.



  • Swordfish schrieb:

    Nana, des passt schon. Ich find's halt ned schön.

    In C++ ist schön, was komplex aussieht und dabei den Code verkürzt. 🤡



  • Es is wurscht. Der Compiler macht eh aus beidem des gleiche.



  • Ich hab nochmal was neues probiert.

    std::string to_hex_string(long long val)
    {
        std::string hex;
        const char inv[] = "0123456789abcdef";
        long long copy, size;
        copy = val;
        for(size = 0; copy /= 10; ++size);
        hex.resize(size);
        do
        {
            hex[size-((copy++)+1)] = inv[val%16];
        } while(val /= 16);
        return hex;
    }
    

    Ich komm zwar nicht an ethon oder camper ran, aber wenigstens check ich meinen Code 🙂

    swordfish: 2000000 calls -> 1.065s
    incocnito: 2000000 calls -> 0.701s
    ethon3: 2000000 calls -> 0.509s
    camper: 2000000 calls -> 0.555s

    //edit
    static weggemacht



  • @Incocnito: Was sollen die static ohne const da? Die verhindern dass deine Funktion Thread Safe ist.



  • also bei mir (msvc cl v16) schaut's eher so aus:

    swordfish       12345678        0.08
    incognito       12345678        0.266
    

  • Mod

    theta schrieb:

    @Incocnito: Was sollen die static ohne const da? Die verhindern dass deine Funktion Thread Safe ist.

    Wichtiger noch: sie sind der Geschwindigkeit wahrscheinlich auch nicht förderlich.
    copy und size würde man normalerweise sowieso in Registern finden, und das static beim string verhindert zwar, dass dieser string jedesmal neu erzeugt werden muss, dafür wird beim return immer kopiert. Wäre der string dagegen automatisch, gäbe beim return nichts zu kopieren.



  • Nagut, dann ohne static.

    @Swordfish
    Scheint wohl so als wär was beim messen schiefgelaufen. Jetzt hab ich auch wieder solche Werte

    incocnito	12345678	0.722
    swordfish	12345678	0.539
    ethon3		12345678	0.516
    camper		12345678	0.494
    

    😞 Ach, was solls...
    Immer noch schneller als vorher.



  • Ich hätte noch Folgendes im Angebot:

    template<typename T>
    std::string to_hex_string_tachyon(T val)
    {
        char const * lut = "0123456789abcdef";
        T const mask(~((~T()) << 4));
        std::string str(sizeof(val) * 2, 0);
        std::string::reverse_iterator i = str.rbegin();
    
        while(val > 16)
        {
            *(i++) = lut[val & mask];
            val >>= 4;
        }
        *i = lut[val & mask];
        return str;
    }
    

  • Mod

    Tachyon schrieb:

    Ich hätte noch Folgendes im Angebot:

    template<typename T>
    std::string to_hex_string_tachyon(T val)
    {
        char const * lut = "0123456789abcdef";
        T const mask(~((~T()) << 4));
        std::string str(sizeof(val) * 2, 0);
        std::string::reverse_iterator i = str.rbegin();
        
        while(val > 16)
        {
            *(i++) = lut[val & mask];
            val >>= 4;
        }
        *i = lut[val & mask];
        return str;
    }
    

    Da bleiben allerdings bei kleinen Zahlen führende '\0' drin stehen.



  • camper schrieb:

    Da bleiben allerdings bei kleinen Zahlen führende '\0' drin stehen.

    Da gabs hier schon mehrere Lösungen, wo das der Fall ist. Da haste Dich auch nicht beschwert.


Anmelden zum Antworten