Laurens Vandenbroucke

Article information: Language: C++, C
Platform: Windows





Preknowledge


in this article i am using:

  • tstring, tstringstream, More information about Unicode strings
  • High-performance timers, Read all about the timers

    Introduction


    Converting a string to float or double can be done in various ways.
    but which method to use?

    In this article ill show you the most common used ways to convert the string,
    and enumerate the pros and cons of the methods.

    How to Compare

    Well, You possibly want your application or game to run as fast as possible,
    so we are comparing the executed/elapsed time of the function.

    To measure the elapsed time of the function we must use a HighPerformanceTimer to get the most accurate timing

    Pseudocode:
    var BeginTime = getTime(); for(int i=0; i<100; ++i) { //this would be our function to benchmark testFunction(); } var ElapsedTime = getTime()-BeginTime;
    in my Timer article i posted a simple HighPerformanceTimer class (PerformanceTimer),
    This is what i will be using to preform these tests.
    #include "Timer.h"
    PerformanceTimer timer;
    timer.Start();
    for(int i=0; i<100; ++i)
    {
    	//this would be our function to benchmark
    	testFunction();
    }
    double dNanoSecondsElapsed = timer.GetElapsedTime();
    
    Ill also test it on a few deferent PC's to get the bigger picture...
  • Windows XP 32-bit SP3 Intel Pentium 4 CPU @1.8GHz
  • Windows Vista 32-bit SP2 Intel Core2 Duo CPU T7500 @2.20GHz
  • Windows 7 Ultimate 64-bit Intel Core i7 CPU 030 @2.80GHz

    As you can see below i have written the examples that the conversion has been preformed 3000 times.
    The seconds with nanosecond precision returned by Timer.GetElapsedTime(); could still be slightly deferent every time you check it.
    Thats why I perform the Test 3000 times (3000*3000 conversions) and took the average to post here.

    String to float conversion


    For the first few examples ill be using the following variables:
    tstring tString = _T("2340.35636684847633");
    const int iCount = 1000;
    I will convert the string to a float value, but if you need for some reason a double. The only thing you need to do is remove the float cast.

    The first method to convert the string to a float is probably the most popular.
    Yes I'm talking about the tstringstream it can simply convert to a datatype by using the bitschift operators:
    int iValue = 0;
    tstringstream tBuffer;	//create the buffer
    tBuffer<<_T("1234");	//insert the Unicode string "1234" into the buffer
    tBuffer>>iValue;	//convert the value from the buffer to the datatype of iValue and insert it in iValue
    //iValue has now the value 1234
    our conversion code look something like this:
    #include "Timer.h"
    PerformanceTimer timer;
    float fReturn = 0;
    
    timer.Start();
    for(int i=0; i<iCount*3; ++i)
    {
    	if(tString.empty())break;
    
    	tstringstream buffer;
    	buffer<<tString;
    	buffer>>fReturn;
    }
    double dElapsedTime = Timer.GetElapsedTime();
    My tstringstream to float conversion results:
    50.995ms 18.415ms 7.668ms
    The second method using _stscanf_s to convert the string to a float:
    Expand/hide "_stscanf_s" usage


    #include "Timer.h"
    PerformanceTimer timer;
    float fReturn=0;
    
    timer.Start();
    for(int i=0; i<iCount*3; ++i)
    {
    	int iRead = _stscanf_s(tString.c_str(), _T("%f"), &fReturn);
    }
    double dElapsedTime = Timer.GetElapsedTime();
    My _stscanf_s to float conversion results:
    11.603ms 6.230ms 3.639ms
    And the Third one :_tstof, my personal favorite and you will see soon why :)
    #include "Timer.h"
    PerformanceTimer timer;
    float fReturn=0;
    
    timer.Start();
    for(int i=0; i<iCount*3; ++i)
    {
    	fReturn = (float)_tstof(tString.c_str());
    }
    double dElapsedTime = Timer.GetElapsedTime();
    My _tstof to float conversion results:
    6.326ms 3.267ms 1.832ms

    String to float3, Vector3 conversion


    This would be when you are reading manually an *.obj (3D model) file or your savefile,...
    So let us assume we got the following string:
    const tstring tVecString = _T("1340.35636684847633 2340.35636684847633 3340.35636684847633");
    The first example we are using the tstringstream again.
    #include "Timer.h"
    PerformanceTimer timer;
    Vector3 vec;
    
    timer.Start();
    for(int i=0; i<iCount; ++i)
    {
    	size_t iEnd = tVecString.find(_T(' '), 0);
    	tstringstream buffer;
    	
    	buffer<<tVecString.substr(0, iEnd);
    	buffer>>vec.x;
    	buffer.str(_T(""));
    	buffer.clear();
    	size_t iOfset = iEnd+1;
    	
    	iEnd = tVecString.find(_T(' '), iOfset);
    	buffer<<tVecString.substr(iOfset, iEnd-iOfset);
    	buffer>>vec.y;
    	buffer.str(_T(""));
    	buffer.clear();
    	iOfset = iEnd+1;
    
    	iEnd = tVecString.find(_T(' '), iOfset);
    	buffer<<tVecString.substr(iOfset, iEnd-iOfset);
    	buffer>>vec.z;
    }
    double dElapsedTime = Timer.GetElapsedTime();

    My tstringstream to float3/Vector3 conversion results:
    39.141ms 14.894ms 7.357ms
    The next example, using _stscanf_s is probably the fastest one to write...
    #include "Timer.h"
    PerformanceTimer timer;
    Vector3 vec;
    
    timer.Start();
    for(int i=0; i<iCount; ++i)
    {
    	int iRead = _stscanf_s(tVecString.c_str(), _T("%f %f %f"), &vec.x, &vec.y, &vec.z);
    }
    double dElapsedTime=Timer.GetElapsedTime();
    My conversion to float3/Vector3 results are:
    12.181ms 5.822ms 3.705ms
    and in the last example ill use _tstof
    #include "Timer.h"
    PerformanceTimer timer;
    Vector3 vec;
    
    timer.Start();
    for(int i=0; i<iCount; ++i)
    {
    	size_t iEnd = tVecString.find(_T(' '), 0);
    	vec.x = (float)_tstof(tVecString.substr(0, iEnd).c_str());
    
    	size_t iOfset = iEnd+1;
    	iEnd = tVecString.find(_T(' '), iOfset);
    	vec.y = (float)_tstof(tVecString.substr(iOfset, iEnd-iOfset).c_str());
    	
    	iOfset = iEnd+1;
    	iEnd = tVecString.find(_T(' '), iOfset);
    	vec.z = (float)_tstof(tVecString.substr(iOfset, iEnd-iOfset).c_str());
    }
    double dElapsedTime = Timer.GetElapsedTime();
    9.837ms 4.645ms 2.592ms

    Conclusion


    I'm not suggesting never to use tstringstream,
    but it should never be used on a location where you need speed.
    For simple sting to float conversions you should use _tstof
    And the _stscanf_s is very good and handy when parsing complex strings. as you could see in the conversion to float3/Vector3 the elapsed time is not increasing much.
    But the use of _tstof is still better for this example.
    If the string would be more complex, the use of _stscanf_s is preferred.
    static const int MAXNAME_SIZE = 100;
    
    //we got a string with multiple values and data types
    tstring tString = _T("Name=TestUser Gender=F Age=23 TimeOnline=752419.1033  5655388588");
    
    //the requested data
    TCHAR tName[MAXNAME_SIZE];
    TCHAR tGender;
    float fTimeOnlile;
    long iScore;
    int iAge;
    
    //pars the string
    int iRead = _stscanf_s(tString.c_str(), _T("Name=%s Gender=%c Age=%d TimeOnline=%f  %d"),
    						tName, sizeof(tName), &tGender, sizeof(tGender), &iAge, &fTimeOnlile, &iScore);
    


  • Light Dark
    Blue Red Grey Purple Green