Browse DevX
Sign up for e-mail newsletters from DevX


Formatting Floating Point Numbers-3 : Page 3




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Going Dotty
The formatting operation consists of locating the decimal point's position. If the number of fractional digits is higher than decplaces, do_fraction() will remove the remaining digits.

To locate the decimal point, use string::find(). Remember that STL algorithms use an agreed-upon constant to report a "value not found" condition. In the case of string, this constant is called string::npos:

char DECIMAL_POINT='.'; // use ',' in European locales size_t n=str.find(DECIMAL_POINT); if ((n!=string::npos)// is there a decimal point? { //check the number of fractional digits }

If there's no decimal point, do_format() returns the string as is. Otherwise, do_format() checks whether the number of fractional digits is higher than decplaces. If it is, the fractional part is truncated:

size_t n=str.find(DECIMAL_POINT); if ((n!=string::npos)// is there a decimal point? &&(str.size()> n+decplaces))//is it followed by at //least decplaces digits? //write nul instead of the first digit after decplaces str[n+decplaces]='\0';

The last line of code overwrites the first superfluous fractional digit. It uses the \0 constant to chop the string at the right place. Notice, however, that string objects' data may contain nul characters; the actual length of the string is determined by the value returned from size(). Therefore, you can't assume that, at this stage, the string is formatted correctly. In other words, if str contained "123.4567" originally, after the insertion of the \0 constant it contains "123.45\07". To shrink str to "123.45", use the self-swapping idiom:

str.swap(string(str.c_str()) );//get rid of spurious //characters after the nul

How does it work? The string::c_str() function returns a const char * representation of the string object. This value is used as the initializer of a temporary string object. Then, the temporary is used as the argument for str.swap(). This swap() call assigns the value "123.45" to str. Some older compilers that don't support default template arguments might not accept this swap call. In this case, use manual swapping instead:

string temp=str.c_str(); str=temp;

This isn't the most elegant code but it gets the job done.

No Strings Attached
Here's the complete code for do_fraction():

string do_fraction(long double value, int decplaces=3) { ostringstream out; int prec= numeric_limits<long double>::digits10; // 18 out.precision(prec);//override default out<<value; string str= out.str(); //extract string from stream size_t n=str.find(DECIMAL_POINT); if ((n!=string::npos) //is there a decimal point? && (str.size()> n+decplaces)) //is it followed by at //least decplaces digits? { str[n+decplaces]='\0';//overwrite first redundant digit } str.swap(string(str.c_str()));//get rid of spurious //characters after the nul return str; }

If you're wary about returning a string object by value, pass a string object by reference as an argument:

void do_fraction(long double value, string & str, int decplaces=3);

Personally, I prefer to trust my compiler for applying this optimization automatically. In addition, using string as the return value enables you to use do_fraction() in the following manner:

cout << funct(123456789.69999001) << '\t' << funct(12.011)<<endl;// //output: 123456789.69 12.01

Danny Kalev is a certified system analyst and software engineer specializing in C++. He was a member of the C++ standards committee between 1997 and 2000 and has since been involved informally in the C++0x standardization process. He is the author of "The ANSI/ISO Professional C++ Programmer's Handbook" and "The Informit C++ Reference Guide: Techniques, Insight, and Practical Advice on C++."
Thanks for your registration, follow us on our social networks to keep up-to-date