Home | Blog | Software | Reviews and Features | Forum | Help | Donate | About us
topbanner_forum
  *

avatar image

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
  • December 04, 2016, 02:19:31 PM
  • Proudly celebrating 10 years online.
  • Donate now to become a lifetime supporting member of the site and get a non-expiring license key for all of our programs.
  • donate

Author Topic: Numeric Format Strings in C++  (Read 7886 times)

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,294
    • View Profile
    • www.StoicJoker.com
    • Donate to Member
Numeric Format Strings in C++
« on: August 15, 2008, 07:55:18 PM »
Greetings
   I have spent the last several months working on a network scanner that will (quickly) search a network for any & all Print devices and then collect information from/about them (It'll do about 20 IP Addresses per second).

   As usual I am working in Pure Win32 API C++ e.g. No cs, No .NET, No MFC. ...and... I've completely hit the wall on one seemingly simple (but key) point.  I can not get the numeric string formatting to output a string like: 1,254.00

I'm passing a dollar amount stored in a double into StringCbPrintf(...) using %0.2f in the format string which gives me 1254.00 ...But I've gota have the blasted coma's in there or the Corporate Board Room types will whine and gnash their teeth.

Anyone have a clue what I'm missing?

Thank you,
Stoic Joker

mwb1100

  • Supporting Member
  • Joined in 2006
  • **
  • Posts: 1,520
    • View Profile
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #1 on: August 15, 2008, 11:59:12 PM »
If you're using streams (cout, stringstream, etc.) to perform the I/O or formatting, what you want to do is call the stream's imbue() method with the proper locale.

As an aside - you should never use floats or doubles to deal with monetary amounts, of course...

Something like:

Code: C++ [Select]
  1. #include <locale>
  2. #include <iostream>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8.     locale loc( ""); // user's locale
  9.     locale prev_loc( cout.imbue( loc)); // set stream's new locale and remember the origial ("global") locale
  10.    
  11.     int dollars = 4561254;
  12.     int cents = 75;
  13.    
  14.     cout << dollars << "." << cents << endl;
  15.    
  16.     cout.imbue( prev_loc);
  17.    
  18.  
  19.     return 0;
  20. }

If you're not using streams (maybe you're a fan of printf-like formatting) then you might try adding the ' (single quote) character to the flags in the format specifier.  Some implementations take that to mean that the output should use thousands separators (Microsoft's compiler does not support this).

If the compiler you're using does not support that extension, the open source Trio library might be an option for you if you want to go this route, since it does support it:

http://daniel.haxx.se/projects/trio/


Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,294
    • View Profile
    • www.StoicJoker.com
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #2 on: August 16, 2008, 10:02:45 AM »
Greetings
   And thanks for the input ... But I'm a bit confused on one point (not to side track myself...). Why not use doubles for dealing with money? The app I working on is the 2nd half of the total project that generates billing reports for 1,000 line items that are all being billed at $00.01 per x. If I used int I'd end up pointer casting everything multiple times and still probably not get the right answer.

   I guess I should also mention that this is a GUI application, so unless I'm badly miss-informed (which has happened...) all the cout stuff is strictly for CLI apps ... Correct?

GetDlgItemInt(...) automatically rounds off the "int" to the nearest whole number so 0.010 becomes 0
GetDlgItemText(...) dumped into atof() (which requires float or double) will actually give me 0.010 as a numeric value that I can then use.

The answer I ended up finding (at 1:30am) was to just run the string through
GetCurrencyFormat(LOCALE_USER_DEFAULT, 0, szNowFormatted, NULL, szString, ARRAYSIZE(szString));
Which is giving me the 1,234.01 format I was after.

Note: I'm using MSVC from MSVS and all the documentation on the MSDN seems to be split between two warring factions:

1. Says to use %n to get the thousands separators.

2. says never use %n because it's "unsafe" and has been depreciated. ...hence my quandary. ...Because none of the aforementioned documentation even hinted at the alternative, which was the API I (am using) mentioned above.

I'm not married to it ... so if there is a better way let me know.

Thank you,
Stoic Joker

mouser

  • First Author
  • Administrator
  • Joined in 2005
  • *****
  • Posts: 36,406
    • View Profile
    • Mouser's Software Zone on DonationCoder.com
    • Read more about this member.
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #3 on: August 16, 2008, 10:13:44 AM »
I had a hard time finding a good essay saying why not to use floats for money, but it's great that mwb raised this issue with you.  It's not an obvious thing, but it is quite important that you avoid using floating point for money if at all possible.

Here's one short piece on why: http://everything2.c...ex.pl?node_id=803770

Basically the problem is that floating point variables were not designed to store exact values at arbitrary precision, and you can get some very unintuitive and weird rounding errors.  In the case of money, you almost always want to deal with an atomic value of 1 cent, and you dont care about fractions of cents, but you DO care about losing a penny here or there.  In such cases you should use an integer variable that holds the number of cents in the amount. (1 dollar stores as 100).  That lets you work with exact integer arithmetic.

mwb1100

  • Supporting Member
  • Joined in 2006
  • **
  • Posts: 1,520
    • View Profile
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #4 on: August 16, 2008, 11:59:57 AM »
What Mouser said about using float/doubles for currency.

As far as some of the other things you brought up:

  • I'm not sure exactly how %n is used to perform thousands grouping, but the reason it's deprecated (generally shouldn't be used) is that it's easy to introduce bugs that trash the stack which can lead to security errors.
  • As far as using streams in GUI applications, you are right that "cout" is something that is pretty much not usable, but you can use stringstreams where the formatted output gets put into a string variable instead of getting sent to stdout (sort of like the difference between printf() and sprintf()).  See the end of this post for my sample modified to use stringstreams.
  • I wasn't aware of GetCurrencyFormat() - that looks to be a pretty good solution. I think I would probably write a wrapper for it that took an int value in cents (or whatever lowest denominator currency you're using) so floats could be avoided, formatted a string with the decimal point in the right place then sent that string off to GetCurrencyFormat() to do the rest of the work.

Just for reference, here's the sample using stringstreams (though I think a GetCurrencyFormat() wrapper is the way I'd go):

Code: C++ [Select]
  1. #include <locale>
  2. #include <sstream>
  3. #include <string>
  4. #include <stdio.h>
  5.  
  6. using namespace std;
  7.  
  8. int main()
  9. {
  10.     stringstream formatted_string;
  11.  
  12.     locale loc( ""); // user's locale
  13.     locale prev_loc( formatted_string.imbue( loc)); // set stream's new locale and remember the origial ("global") locale
  14.    
  15.     int dollars = 4561254;
  16.     int cents = 75;
  17.    
  18.    
  19.     formatted_string << dollars << "." << cents << endl;
  20.    
  21.     printf( "%s\n", formatted_string.str().c_str());
  22.    
  23.     formatted_string.imbue( prev_loc);
  24.  
  25.     return 0;
  26. }

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,294
    • View Profile
    • www.StoicJoker.com
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #5 on: August 16, 2008, 12:15:22 PM »
In the case of money, you almost always want to deal with an atomic value of 1 cent, and you dont care about fractions of cents, but you DO care about losing a penny here or there.  In such cases you should use an integer variable that holds the number of cents in the amount. (1 dollar stores as 100).  That lets you work with exact integer arithmetic.

Hm... Most of the Bean Counter types seem to work 4 decimal places out when doing cost projections or profit analysis stuff, I really don't see how I could use a whole number for that (Multiply it by 1,000.000? ...Zoiks!).

So far I haven't had a problem (but I'm not writing code for Sun), and there really isn't any way for that to adversely impact the output of the program I'm working on. But I will keep it in mind in the future.

(Strange) Side Note: My son took a programming class in college and his professor was insistant on their using the double data type for doing money calculations. So if they're teaching kids mistakes comming outa the gate it's no wonder half the software out there is crap...

*Shrug* ...Points to Ponder...

Thank you,
Stoic Joker

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,294
    • View Profile
    • www.StoicJoker.com
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #6 on: August 16, 2008, 12:33:21 PM »
@mwb1100

I knew the why %n was deprocated, I Just can't figure out why the documentation just keept looping instead of point out to the new "right" answer. ...And yes I did do a quicky wrapper for both GetCurrencyFormat(...) and GetFormat(...) (and a customized Locale) Which gives me exactly what I needed for the output (billing) report.

Does stringstream allow for (inherantly) secure buffer handling? That's why I'm using the StringCbXxxx(...) series of functions. They do automatic buffer checking to prevent over-runs ... and will even flag you at compile time if something has potential of going poof! I keep thinking that f0dder was the one that had brought them to my attention ... but I'm not sure.


I'll save the code sample in my notes as it's bound to come it handy for something down the line.

Thank you,
Stoic Joker

mwb1100

  • Supporting Member
  • Joined in 2006
  • **
  • Posts: 1,520
    • View Profile
    • Donate to Member
Re: Numeric Format Strings in C++
« Reply #7 on: August 16, 2008, 06:21:45 PM »
Does stringstream allow for (inherantly) secure buffer handling? That's why I'm using the StringCbXxxx(...) series of functions. They do automatic buffer checking to prevent over-runs ... and will even flag you at compile time if something has potential of going poof!

The stringstream class is similar to the std::string class or the MFC/ATL CString class in that all of the buffer management is handled by the class, so there's no chance of a buffer overflow (unless you go out of your way to do evil things to the object).  If you're using C++, these classes are definitely the way to go over raw  C "char *" strings whenever possible.