84

I want to write a method that will take an integer and return a std::string of that integer formatted with commas.

Example declaration:

std::string FormatWithCommas(long value);

Example usage:

std::string result1 = FormatWithCommas(7800);
std::string result2 = FormatWithCommas(5100100);
std::string result3 = FormatWithCommas(201234567890);
// result1 = "7,800"
// result2 = "5,100,100"
// result3 = "201,234,567,890"

What is the C++ way of formatting a number as a string with commas?

(Bonus would be to handle doubles as well.)

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
User
  • 62,498
  • 72
  • 186
  • 247
  • possible duplicate of [How to insert spaces in a big number to make it more readable?](http://stackoverflow.com/questions/7257956/how-to-insert-spaces-in-a-big-number-to-make-it-more-readable) – Rob Kennedy Sep 01 '11 at 22:09
  • 1
    possible duplicate of [How do you set the cout locale to insert commas as thousands separators?](http://stackoverflow.com/questions/4728155/how-do-you-set-the-cout-locale-to-insert-commas-as-thousands-separators) – Matthieu M. Sep 02 '11 at 07:16
  • 1
    what's lame about these duplicate statements is that I searched for this question before using the most obvious search terms and did not find either of those questions. My title is better and more to the point and I like the accepted answer to my question better than the answers to any of those. – User Sep 02 '11 at 14:09
  • If high performance is an issue you can see my related question: [How can I improve formatting number with commas performance?](http://stackoverflow.com/questions/7693751/how-can-i-improve-formatting-number-with-commas-performance) – User Oct 25 '12 at 18:28

14 Answers14

64

You can do as Jacob suggested, and imbue with the "" locale - but this will use the system default, which does not guarantee that you get the comma. If you want to force the comma (regardless of the systems default locale settings) you can do so by providing your own numpunct facet. For example:

#include <locale>
#include <iostream>
#include <iomanip>

class comma_numpunct : public std::numpunct<char>
{
  protected:
    virtual char do_thousands_sep() const
    {
        return ',';
    }

    virtual std::string do_grouping() const
    {
        return "\03";
    }
};

int main()
{
    // this creates a new locale based on the current application default
    // (which is either the one given on startup, but can be overriden with
    // std::locale::global) - then extends it with an extra facet that 
    // controls numeric output.
    std::locale comma_locale(std::locale(), new comma_numpunct());

    // tell cout to use our new locale.
    std::cout.imbue(comma_locale);

    std::cout << std::setprecision(2) << std::fixed << 1000000.1234;
}
Node
  • 3,443
  • 16
  • 18
62

Use std::locale with std::stringstream

#include <iomanip>
#include <locale>

template<class T>
std::string FormatWithCommas(T value)
{
    std::stringstream ss;
    ss.imbue(std::locale(""));
    ss << std::fixed << value;
    return ss.str();
}

Disclaimer: Portability might be an issue and you should probably look at which locale is used when "" is passed

Jacob
  • 34,255
  • 14
  • 110
  • 165
  • @Rob Kennedy: http://stackoverflow.com/questions/4406895/what-stdlocale-names-are-available-on-common-windows-compilers – Jacob Sep 01 '11 at 22:14
  • 4
    This function did not put commas for me. What locale should I set? Which locale should I ask my users to set? Fail. – Jonny May 28 '15 at 09:35
  • 1
    Answer is not complete without example of using a specific locale. Getting it to work requires learning the entire locale machinery. – Reb.Cabin Sep 01 '15 at 13:13
  • More specifically, the answer did disclaim that portability might be an issue and you should probably look at what locale is used when "" is passed. It turns out that this answer does not work out-of-the-box on my mac, but "looking at what locale is used" requires going down the locale rabbit-hole. See this question for a better answer that works out of the box: http://stackoverflow.com/questions/3479485/ – Reb.Cabin Sep 01 '15 at 13:29
  • Spits out the number with commas in the UK in 2021 with 'gcc version 10.3.0 (Ubuntu 10.3.0-1ubuntu1)' – ahcox Sep 02 '21 at 23:25
  • 1
    using "en_US.UTF-8" as the locale results in commas. – alwaysmpe Jan 07 '22 at 17:00
  • using just "en_US" or "en_UK" does as well – nfries88 Feb 26 '23 at 11:41
  • 1
    "", "en_US.UTF-8", "en_US" and "en_UK" all don't work for me, they throw a runtime_error saying the name is not valid. Using mingw-w64 g++ Windows 11. – Lily-Heather Crawford Feb 27 '23 at 07:27
  • YOU MUST NOT USE THIS!! - this sample founded on side effect of MSVC++ compiler BUG! This is not work for other compilers. MOREOVER std::fixed manipulator affects only on float types but not on integer types see https://developercommunity.visualstudio.com/t/std::fixed-affects-to-integer-output-of/10312687 – fsmoke Mar 16 '23 at 14:28
42

I consider the following answer to be easier than the others:

#include <iostream>
int main() {
   int v = 7654321;
   auto s = std::to_string(v);

   int n = s.length() - 3;
   int end = (v >= 0) ? 0 : 1; // Support for negative numbers
   while (n > end) {
      s.insert(n, ",");
      n -= 3;
   }
   std::cout << (s == "7,654,321") << std::endl;
}   

This will quickly and correctly insert commas into your string of digits.

AlainD
  • 5,413
  • 6
  • 45
  • 99
carljalal
  • 637
  • 6
  • 16
  • this will not work with values prefixed with zeroes like 010100 – PapaDiHatti May 06 '17 at 00:32
  • @Homer6 The problem with negative numbers could be overcome by a minor adjustment to the code. If the number is negative, the while loop criterion should be insertPosition>1 ... for -106 insertPosition would start at 1 and no comma would be inserted. – cardiff space man Jun 14 '17 at 19:27
  • @Kapil numbers prefixed with zeros such as 010100 would work: you'd get insertPosition == 3 to start, your comma would go between the 3rd and 4th digits and that's that. Could you further explain how such a string of digits would fail? – cardiff space man Jun 14 '17 at 19:30
  • @arljalal I like the code a lot. It's only flaw in my opinion is if really long numbers are common it is O(length squared). The while loop runs O(length) times and each time it transfers O(length) digits. An algorithm that works on comma-separated blocks could be O(length) overall. Most of us will be formatting 32-bit or 64-bit numbers so the issue is minor. – cardiff space man Jun 14 '17 at 19:33
  • 1
    @cardiffspaceman The largest number you can store in a uint64_t is 18,446,744,073,709,551,615. That's 20 digits. If you're working with numbers bigger than that, and actually want them to be readable, scientific notation is probably the way to go. The 18 digit number is hard enough to read. tl;dr: I don't think big O runtime is really relevant here. Perhaps if you're working with numbers that are millions of digits long and you require precision down to the one's place. – Justin T Conroy Sep 23 '21 at 18:31
2

If you are using Qt, you can use this code:

const QLocale& cLocale = QLocale::c();
QString resultString = cLocale.toString(number);

Also, do not forget to add #include <QLocale>.

NG_
  • 6,895
  • 7
  • 45
  • 67
2

This is pretty old school, I use it in large loops to avoid instantiating another string buffer.

void tocout(long a)
{
    long c = 1;

    if(a<0) {a*=-1;cout<<"-";}
    while((c*=1000)<a);
    while(c>1)
    {
       int t = (a%c)/(c/1000);
       cout << (((c>a)||(t>99))?"":((t>9)?"0":"00")) << t;
       cout << (((c/=1000)==1)?"":",");
    }
}
Dan Bechard
  • 5,104
  • 3
  • 34
  • 51
Tom Serink
  • 29
  • 2
  • I like that (outside of the lack of spaces between operators). Although divisions by 1,000 are probably fast on newer processors, you could allocate a buffer on the stack and generate the number in reverse and print each character and every 3 you also output a comma... – Alexis Wilke Jul 13 '20 at 04:41
1

based on the answers above, I ended up with this code:

#include <iomanip>
#include <locale> 

template<class T>
std::string numberFormatWithCommas(T value){
    struct Numpunct: public std::numpunct<char>{
    protected:
        virtual char do_thousands_sep() const{return ',';}
        virtual std::string do_grouping() const{return "\03";}
    };
    std::stringstream ss;
    ss.imbue({std::locale(), new Numpunct});
    ss << std::setprecision(2) << std::fixed << value;
    return ss.str();
}
  • this invokes undefined behavior (**double free or corruption** in my test) because you're passing a pointer to a facet that wasn't allocated by `new`. Either use `new` as in other answers, or set base class refcount to 1 in the constructor of your facet! – Cubbi Sep 11 '13 at 17:55
  • Thanks for pointing out. I tested it only on iOS where it worked. It didn't work for mac. – Radif Sharafullin Sep 12 '13 at 04:10
1

I found the solution! just copy this to one of your function, this function is written in static function.

// Convert 100000000 to 100,000,000, put commas on the numbers!

std::string AppManager::convertNumberToString(int number) {
    std::string s = std::to_string(number);
    std::string result = "";
    std::string tempResult = "";
    unsigned long n = s.length() - 3;
    int j = 0;
    for (int i=s.size()-1; i>=0; i--) {
        if (j%3 == 0) {
            result.append(",");
        }
        result.append(s, i, 1);
        j++;
    }
    
    result = result.substr(1, result.size()-1);
    
    //now revert back
    for (int i=result.size()-1; i>=0; i--) {
        tempResult.append(result, i, 1);
    }
    
    return tempResult;
}

Here is the result of those code:

click here for seeing the result above!

Vincent Doba
  • 4,343
  • 3
  • 22
  • 42
1

I have seen so many ways of doing this, reversing the string(Twice!), using setlocale(sometimes works sometimes not) This is a template solution, I then add explicit specializations. This works for char*, wchar*, string and wstring. I dont translate from numeric to string format here, I highly recommend to_string and to_wstring they are way faster than the 'C' functions such as _itoa etc...

    template<typename T, typename U>
    T StrFormatNumber(const T Data) {
const size_t Length = Data.length();
assert(Length > 0);
// if( 0 == Length ) I would log this and return 
if (Length < 4) { // nothing to do just return
    return Data;
}
constexpr size_t buf_size{ 256 };
assert(((Length)+(Length / 3)) + 1 < buf_size);
if (((Length)+(Length / 3)) + 1 >= buf_size) {
     throw std::invalid_argument( "Input buffer too large" );
}
std::array<U, buf_size > temp_buf{};
auto p{ 0 };
temp_buf[0] = Data[0];
for (auto y{ 1 }; y < Length; y++) {
    if ((Length - y) % 3 == 0) {
        temp_buf[y + p] = ',';
        p++;
    }
    temp_buf[(y + p)] = Data[y];
}
return temp_buf.data();
}
    template<typename T = const char*>
    std::string StrFormatNum(const char* Data) {
        return StrFormatNumber<std::string, char>(std::string(Data));
    }
    template<typename T= std::string>
    std::string StrFormatNum(const std::string Data) {
         return StrFormatNumber<std::string, char>(Data);
     }
    template<typename T = std::wstring>
    std::wstring StrFormatNum( const std::wstring Data) {
        return StrFormatNumber<std::wstring, wchar_t>(Data);
    }
    template<typename T = const wchar_t*>
    std::wstring StrFormatNum( const wchar_t* Data) {
        return StrFormatNumber<std::wstring, wchar_t>(std::wstring(Data));
    }

    void TestStrFormatNumber() {
        constexpr auto Iterations{ 180 };
        for (auto l{ 0 }; l < Iterations; l++)
        {
            {  // std::string
                std::string mystr{ "10" };
                for (int y{ 0 }; y < Iterations; y++) {

                    mystr += "1";
                    auto p = mystr.length();
                    std::cout << "\r\n  mystr      = " 
                              << std::setw(80) << mystr.c_str()
                              << "\r\n  Length     = "
                              << std::setw(10) << p
                              << "\r\n  modulo % 3 = "
                              << std::setw(10)
                              << p % 3 << "     divided by 3 = "
                              << std::setw(10) << p / 3
                              << "\r\n  Formatted  = " << 
                    StrFormatNum((mystr)).c_str() << "\n";
                }
            }
            {  // std::wstring
                std::wstring mystr{ L"10" };
                for (int y{ 0 }; y < Iterations; y++)
                {
                     mystr += L"2";
                     auto p = mystr.length();
                     std::wcout << "\r\n  mystr      = "
                                << std::setw(80) << mystr.c_str()
                                << "\r\n  Length     = "
                                << std::setw(10) << p
                                << "\r\n  modulo % 3 = "
                                << std::setw(10) << p % 3
                                << "     divided by 3 = "
                                << std::setw(10) << p / 3
                                << "\r\n  Formatted  = "
                                << StrFormatNum((mystr)).c_str()
                                << "\n";
                        }
                    }
                    {   // char*
                        std::string mystr{ "10" };
        for (int y{ 0 }; y < Iterations; y++) {
            mystr += "3";
            auto p = mystr.length();
            std::cout << "\r\n  mystr      = " << std::setw(80) << mystr.c_str()
                << "\r\n    Length     = " << std::setw(10) << p
                << "\r\n  modulo % 3 = " << std::setw(10) << p % 3 << "     divided by 3 = " << std::setw(10) << p / 3
                << "\r\n  Formatted  = " << StrFormatNum((mystr.c_str())).c_str() << "\n";
        }
    }
    {  // wchar*
        std::wstring mystr{ L"10" };
        for (int y{ 0 }; y < Iterations; y++) {
            mystr += L"4";
            auto p = mystr.length();
            std::wcout << "\r\n  mystr      = " << std::setw(80) << mystr.c_str()
                << "\r\n  Length     = " << std::setw(10) << p
                << "\r\n  modulo % 3 = " << std::setw(10) << p % 3 << "     divided by 3 = " << std::setw(10) << p / 3
                << "\r\n  Formatted  = " << StrFormatNum((mystr.c_str())).c_str() << "\n";
        }
    } 
}

}

I have tested up to 1,000 spaces(With a larger buffer of course)

  • temp_buf[y + p] arithmetic overflow... – Mecanik Mar 03 '22 at 15:10
  • I've tested this code thousands of times I'm using visual c++22 and I used to use it on visual c++ 19 and I have never experienced this I literally put it on loops of thousands going anywhere from one to hundreds of places. What environment are you using that this happened I'm very curious – Richard Day Mar 04 '22 at 16:59
  • I`m using latest C++ as well and VS2019, but the platform is x64 bits. – Mecanik Mar 04 '22 at 17:08
  • I'll try it again when I get home. – Richard Day Mar 05 '22 at 19:39
  • I just tried debug and release, again using visual C++ 2022. My test function is too long to enter here... I can not replicate your error please demonstrate your code. – Richard Day Mar 06 '22 at 02:54
  • Also I AM using x86_64 as well. – Richard Day Mar 06 '22 at 03:06
  • Sorry but I did not change anything, just copy pasted it into a header file for usage. I cannot show you my code as it's not really relevant. The warning comes from visual studio, so might be just some bug in the end. Function works great by the way, which is why I upvoted. – Mecanik Mar 06 '22 at 04:51
  • 1
    Okay thanks – Richard Day Mar 07 '22 at 18:16
0

Make another solution:

#include <stdio.h>
#include <string>
#include <stdint.h>
#include <inttypes.h>

std::string GetReadableNum(uint64_t n)
{
    std::string strRet;
    char szTmp[256] = { 0 };
    int ccWritten = sprintf(szTmp, "%" PRIu64 "", n);
    if (ccWritten > 0)
    {
        int nGroup = (ccWritten + 2) / 3;
        int nReminder = ccWritten % 3;
        
        strRet.reserve(ccWritten + (nGroup -1) * 3 + 1);
        const char* p = szTmp;
        for (int i = 0; i < nGroup; i++)
        {
            if (nGroup > 1 && i > 0)
                strRet.append(1, ',');

            for (int c = 0; c < (i > 0 || nReminder == 0 ? 3 : nReminder); c++)
                strRet.append(1, *p++);
        }
    }

    return strRet;
}

int main(int argc, const char* argv[])
{
    uint64_t a = 123456789123ULL;

    std::string s = GetReadableNum(a);

    printf("%s\n", s.c_str());

    return 0;
}
ravin.wang
  • 1,122
  • 1
  • 9
  • 26
0

Here is a very simple way to get what you are asking for. You will need a long long to handle result3 201+ billion. The function formatWithCommas() takes in a long long and returns a formatted string with commas at every third decimal place. Use string method to_string() from the standard string library to convert your number to a string. Then use string.length() to get the length of your numeric string. Setup a for loop that starts at the length of your string minus three. Use another function from the string library insert() to place a comma at every third position as you decrement by three, i -= 3. Notice that I use a string "," instead of a character ','. If you don't change datatypes you only need two arguments, the index and in this case a sub-string. If you change datatypes you will need to use a third argument to state how many characters you want to place starting at the given index. Here is an example, str.insert(i, ",") or str.insert(i, ',', 1). The conditional section of your loop i > 0 will prevent the loop from running on a string with less than four characters as str.length() -3 must be at least four to start with making i equal to one for i > 0 to be a true statement.

If you need to work with possibly negative numbers you can set a bool flag and manipulate index zero before the for loop and insert the negative symbol - back after the commas have been correctly placed before returning the formatted numeric string.

/*
Program:Format Number With Comma
Author:Phillip McCullough
Date:1/29/2023
C/Cpp:11+ ANSI Standard

This program uses the function formatWithCommas() to add one or more commas at
every third decimal place as conventional written formatting includes.
-------10--------20--------30--------40--------50--------60--------70--------80
*/
#include <iostream> 
#include <string> 

std::string formatWithCommas(long long number);

int main()
{ 
    std::cout << formatWithCommas(7800) << '\n'// result1 = 7,800
        << formatWithCommas(5100100) << '\n'// result2 = 5,100,100
        << formatWithCommas(201234567890) << '\n';// result3 = 201,234,567,890

    return 0;
}

// ---------------------------------------------------------------------------- 

std::string formatWithCommas(long long number)
{
    std::string number_str = std::to_string(number);

    for (int i = number_str.length() - 3; i > 0; i -= 3)
        number_str.insert(i, ",");

    return number_str;
}

// ----------------------------------------------------------------------------
Phillip
  • 1
  • 4
0

Based on Jacob's answer above...

This is a Windows solution.

// Compiler : Visual Studio 2022
// I tested on Windows 10 64bit
// DATE: 2022.Sep.15

#include <windows.h>
#include <string>
#include <locale>
#include <sstream>

using namespace std;

template<class T>
std::string FormatWithCommas(T value)
{    
    std::stringstream ss;

    // otherwise, use "en_US.UTF-8" as the locale name
    ss.imbue(std::locale("ko_KR.UTF-8"));
    ss << std::fixed << value;

    return ss.str();
}

int main()
{
    LARGE_INTEGER filesize;

    filesize.QuadPart = 1234591046976109;

    // This only works for ASCII strings
    string aaa = (FormatWithCommas(filesize.QuadPart)).c_str();
    std::wstring widestr = std::wstring(aaa.begin(), aaa.end());
    const wchar_t* widecstr = widestr.c_str();

    wprintf_s(L"\nFile Size =  %s bytes\n", widecstr);

    getchar();
    return 0;
}

The execution result is as follows.

The execution result is as follows.

riPmAn
  • 31
  • 2
0

Inspired by @carljalal's answer, I found a similar-but-different answer using reverse iterators. Probably less efficient, but a bit more compact:

#include <string>
#include <iterator>
#include <cctype>  // for isdigit
...

void addCommas(std::string& num)
{
   // begin at the 3rd digit from the back, advance 3 each time
   for (auto it = str.rbegin()+3; it < str.rend(); it += 3)
   {
      // this handles a negative sign (ignores it)
      if (isdigit(*it))
      {
         // there are currently no string functions dealing with reverse_iterators,
         // so use .base() to get it's corresponding forward_iterator

         // inserting into a string invalidates any iterators, so "reset" `it`, and
         // we have to make the return value into a reverse_iterator
         it = std::make_reverse_iterator(str.insert(it.base(), ','));
      }
   }
}

Note, there is some wariness using .base(), but all seems to be well for the inputs I tried. Above handles any length number string, with or without a preceding negative sign, but does not handle decimals. Also note that std::make_reverse_iterator requires c++14 or later.

Some sample inputs and outputs:

1 ==>                  1
12 ==>                 12
123 ==>                123
-12 ==>               -12
-123 ==>              -123
1234 ==>               1,234
-1234 ==>             -1,234
12134 ==>              12,134
-12134 ==>            -12,134
328947328949893 ==>    328,947,328,949,893
-328947328949893 ==>  -328,947,328,949,893
9328947328949893 ==>   9,328,947,328,949,893
-9328947328949893 ==> -9,328,947,328,949,893

Demonstration

yano
  • 4,827
  • 2
  • 23
  • 35
0

Right solution is

struct comma_out : std::numpunct<char>
{
    char do_thousands_sep()   const { return ','; }  // separate with spaces
    std::string do_grouping() const { return "\3"; } // groups of 3 digit
};
 
int main()
{
    std::cout << "default locale: " << 12345678 << '\n';
    std::cout.imbue(std::locale(std::cout.getloc(), new comma_out));
    std::cout << "locale with modified numpunct: " << 12345678 << '\n';
}

YOU MUST NOT USE std::fixed - it's only for floating point

fsmoke
  • 135
  • 6
-1

To make it more flexible, you could construct the facet with a custom thousands sep and grouping string. This way you can set it at runtime.

#include <locale>
#include <iostream>
#include <iomanip>
#include <string>

class comma_numpunct : public std::numpunct<char>
{
public:
   comma_numpunct(char thousands_sep, const char* grouping)
      :m_thousands_sep(thousands_sep),
       m_grouping(grouping){}
protected:
   char do_thousands_sep() const{return m_thousands_sep;}
   std::string do_grouping() const {return m_grouping;}
private:
   char m_thousands_sep;
   std::string m_grouping;
};

int main()
{

    std::locale comma_locale(std::locale(), new comma_numpunct(',', "\03"));

    std::cout.imbue(comma_locale);
    std::cout << std::setprecision(2) << std::fixed << 1000000.1234;
}
Marcel
  • 5
  • 2
Marcel
  • 1
  • 1