C++ cout Digit separator

Below given program outputs following pi value:
PI Value 3.1415926535

Any clues for simple way to output decimal numbers using digit separator as shown below using latest C++ features please?

PI Value 3.141,592,653,5

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    const double my_pi = 3.1415926535;
    cout << fixed;
    cout << setprecision(10);
    cout << "PI Value " << my_pi << endl;
    return 0;
}
Last edited on
perhaps you may touch the locale.
¿where is that format used? (¿and what's the rationale behind it? I don't see how is useful to get the number of decimals at first glance)
I added locale to the code. Still not showing digits separator?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <iomanip>
#include <locale>
using namespace std;
int main()
{
    const double my_pi = 3.1415926535;
    locale our_local("");
    cout << fixed;
    cout << setprecision(10);
    cout.imbue(our_local);
    cout << "PI Value " << my_pi << endl;
    return 0;
}
Why don't you convert the number into a string and insert the commas by hand ?
> locale our_local("");

"" would set the environment's default locale.
perhaps you didn't notice it, but we may not have the same locale as you. If you want help then we need to be able to reproduce your issue.
Last edited on
Why don't you convert the number into a string and insert the commas by hand ?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>
using namespace std;


string separate( double x, int p, char separator )
{
   const string digits = "0123456789";

   stringstream ss;                                                           // Put output into string s via a stringstream
   string s;
   ss << showpoint << setprecision( p ) << x;
   s = ss.str();

   // Number of separators
   int posPoint = s.find( '.' );                                              // position of decimal point in s
   int digitsFront = posPoint - s.find_first_of( digits );                    // # digits ahead of decimal point
   int digitsBack  = s.find_last_of( digits ) - posPoint;                     // # digits after decimal point
   int sepFront = digitsFront / 3;   if ( digitsFront % 3 == 0 ) sepFront--;  // # separators ahead of decimal point
   int sepBack  = digitsBack  / 3;   if ( digitsBack  % 3 == 0 ) sepBack--;   // # separators after decimal point

   // Compose separated string
   int pos = posPoint - 3 * sepFront;                                         // position of first separator
   string result = s.substr( 0, pos );                                        // preceding string
   for ( int i = 1; i <= sepFront; i++ )
   {
      result += separator + s.substr( pos, 3 );                               // keep adding separator and 3 digits
      pos += 3;
   }
   result += '.';                                                             // add decimal point
   pos++;
   for ( int i = 1; i <= sepBack; i++ )
   {
      result += s.substr( pos, 3 ) + separator;                               // keep adding 3 digits and a separator
      pos += 3;
   }
   result += s.substr( pos );                                                 // final bit of string
   return result;
}


int main()
{
   const char separator = ',';

   int prec = 12;
   cout << "Input precision: ";   cin >> prec;

   double pi = 4.0 * atan( 1.0 );
   double piBillion = pi * 1000000000;

   cout << '\n';
   cout << "pi = " << setprecision( prec ) << pi << '\n';
   cout << "pi = " << separate( pi, prec, separator ) << '\n';
   cout << '\n';
   cout << "pi * billion = " << setprecision( prec ) << piBillion << '\n';
   cout << "pi * billion = " << separate( piBillion, prec, separator ) << '\n';
}



EDIT: better version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>
using namespace std;

//======================================================================

string separate( double x, int p, int group, char separator )
{
   const string digits = "0123456789";

   stringstream ss;                                                      // put output into string s via a stringstream
   ss << showpoint << setprecision( p ) << x;
   string s = ss.str();

   // Number of separators
   int posPoint = s.find( '.' );                                                         // position of decimal point in s
   int digitsFront = posPoint - s.find_first_of( digits );                               // # digits before decimal point
   int digitsBack  = s.find_last_of( digits ) - posPoint;                                // # digits after decimal point
   int sepFront = digitsFront / group;   if ( digitsFront % group == 0 ) sepFront--;     // # separators before decimal point
   int sepBack  = digitsBack  / group;   if ( digitsBack  % group == 0 ) sepBack--;      // # separators after decimal point
   
   // Compose separated string
   for ( int i = 1; i <= sepFront; i++ ) s.insert( posPoint - i *   group      , 1, separator );   // separators before point
   posPoint += sepFront;                                                                           // adjust decimal point
   for ( int i = 1; i <= sepBack ; i++ ) s.insert( posPoint + i * ( group + 1 ), 1, separator );   // separators after point

   return s;
}

//======================================================================

int main()
{
   const char separator = ',';
   const int group = 3;

   int prec;
   cout << "Input precision: ";   cin >> prec;

   double pi = 4.0 * atan( 1.0 );
   double piBillion = pi * 1000000000;

   cout << '\n';
   cout << "pi = " << setprecision( prec ) << pi << '\n';
   cout << "pi = " << separate( pi, prec, group, separator ) << '\n';
   cout << '\n';
   cout << "pi * billion = " << setprecision( prec ) << piBillion << '\n';
   cout << "pi * billion = " << separate( piBillion, prec, group, separator ) << '\n';
}



Input precision: 12

pi = 3.14159265359
pi = 3.141,592,653,59

pi * billion = 3141592653.59
pi * billion = 3,141,592,653.59


The fact that you could write numbers like this ... doesn't mean that you should.


I haven't tried it - as I wouldn't do it - but this site suggests grouping:
http://www.cplusplus.com/reference/locale/numpunct/grouping/
Last edited on
Thank you very much @lastchance
Again thank you for the excellent function "separate" and the grouping link.
I will go though this information. Best regards.
Hi @AlexCantor

I tried the locale-based grouping approach and it successfully groups digits in front of the decimal point ... and doesn't group them after!

I hope someone can explain this - it seems irrational (no pun intended).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <string>
#include <iomanip>
#include <locale>
using namespace std;


struct separated : numpunct<char>
{
   string do_grouping() const { return "\03"; }
};


int main()
{
    const double my_pi = 3.1415926535;
    locale our_local( cout.getloc(), new separated );
    cout.imbue( our_local );
    cout << fixed;
    cout << setprecision( 10 );
    cout << "pi: " << my_pi << endl;
    cout << "1000000*pi: " << my_pi * 1000000 << endl;
    return 0;
}


pi: 3.1415926535
1000000*pi: 3,141,592.6535000000

Last edited on
> and doesn't group them after!
Because that makes no sense, there is no need to count digits after the decimal point.
¿do you do it? ¿have you seen it somewhere?

I tought that you realize that when you said
> The fact that you could write numbers like this ... doesn't mean that you should.
> and doesn't group them after!
Because that makes no sense, there is no need to count digits after the decimal point.
¿do you do it? ¿have you seen it somewhere?

I tought that you realize that when you said
> The fact that you could write numbers like this ... doesn't mean that you should.



I tried to code up the problem because that was the problem posed by the original poster! It is not my business to ask why.

I am surprised at the inconsistent nature of grouping by the locale - done on one side of the decimal point, not on the other.

I don't do it myself (although I do occasionally use spaces as a 3-digit separator - on both sides of a decimal point) because it causes potential confusion: in many countries a comma is used as decimal point, it is easily to confuse with a dot when badly written or photocopied, and there are huge risks in being out by a factor of 1000. I use scientific notation to avoid too many zeros neighbouring a decimal point. That is not the point - I was simply addressing the problem posed and I was surprised that the method based on a locale did not work as expected.
I am surprised at the inconsistent nature of grouping by the locale - done on one side of the decimal point, not on the other.

That is indeed how it is defined, in http://eel.is/c++draft/locale.numpunct#2 , quoting the relevant parts of the grammar:

units     ::= digits [thousands-sep units]
digits    ::= digit [digits]
floatval ::= [sign] units [decimal-point [digits]] [e [sign] digits]


separators are part of units, not part of digits, and units appear to the left of decimal-point

as for why.. it appears there is no widespread convention about grouping digits in the fractional parts of floating-point numbers.

Unicode actually allows this: http://unicode.org/reports/tr35/tr35-numbers.html#Number_Pattern_Character_Definitions , recommending U+202F as the thousands separator for the fractional part, referring to http://physics.nist.gov/cuu/Units/checklist.html (rule #16), but I don't think any programming language follows that. Even in math, this post on math.stackexchange is saying the only convention they've seen was groups of 5: https://math.stackexchange.com/questions/182775
Last edited on
Thank you all for your inputs.
Number of years back a scientist (theoretical mathematician & himself a FORTRAN programmer was working on Cray Super Computer at UCLA, Los Angeles & Jet Propulsion Lab (JPL)) working on a classified project
asked me (a junior computer programmer at that time) for such a function.
I wrote using Microsoft Quick C compiler. I was not told what the application was.

Recently, I read the following information about Java language feature:

https://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html

If you want a DecimalFormat object for a nondefault Locale,
you instantiate a NumberFormat and then cast it to DecimalFormat.
Here's an example:

NumberFormat nf = NumberFormat.getNumberInstance(loc);
DecimalFormat df = (DecimalFormat)nf;
df.applyPattern(pattern);
String output = df.format(value);
System.out.println(pattern + " " + output + " " + loc.toString());
Running the previous code example results in the output that follows.
The formatted number, which is in the second column, varies with Locale:

###,###.### 123,456.789 en_US
###,###.### 123.456,789 de_DE
###,###.### 123 456,789 fr_FR


Below link has some information about grouping:
https://en.wikipedia.org/wiki/Decimal_mark

Above link has the following sentence:

In some countries,
these "digit group separators" are only employed to the left of the decimal mark;
in others, they are also used to separate numbers with a long fractional part.

Last edited on
Topic archived. No new replies allowed.