1

this is my first time posting a question. I was hoping to get some help on a very old computer science assignment that I never got around to finishing. I'm no longer taking the class, just want to see how to solve this.

Read in an integer (any valid 64-bit integer = long long type) and output the same number but with commas inserted.

If the user entered -1234567890, your program should output -1,234,567,890. Commas should appear after every three significant digits (provided more digits remain) starting from the decimal point and working left toward more significant digits. If the number entered does not require commas, do not add any. For example, if the input is 234 you should output 234. The input 0 should produce output 0. Note in the example above that the number can be positive or negative. Your output must maintain the case of the input.

I'm relatively new to programming, and this was all I could come up with:

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
  long long n;
  cout << "Enter an integer:" << endl;

    cin >> n;

        int ones = n % 10;
        int tens = n / 10 % 10;
        int hund = n / 100 % 10;
        int thous = n / 1000 % 10;
        int tthous = n / 10000 % 10;

    cout << tthous << thous << "," << hund << tens << ones << endl;

  return 0;
}

The original assignment prohibited the use of strings, arrays, and vectors, so please refrain from giving suggestions/solutions that involve these. I'm aware that some sort of for-loop would probably be required to properly insert the commas in the necessary places, but I just do not know how to go about implementing this.

Thank you in advance to anyone who offers their help!

soulgood
  • 11
  • 2
  • 1
    Unrelated Fun fact: `long long` is not always 64 bits. [It could be longer.](https://en.cppreference.com/w/cpp/language/types) – user4581301 Sep 24 '19 at 01:09

3 Answers3

1

The easy solution would be to use ios::imbue to set a locale that would do all the work for you:

std::cout.imbue(std::locale(""));
std::cout << n << std::endl;

However, if the restraints don't allow for strings or vectors I doubt that this would be a valid solution. Instead you could use recursion:

void print(long long n, int counter) {
    if (n > 0) {
        print(n / 10, ++counter);
        if (counter % 3 == 0) {
            std::cout << ",";
        }
        std::cout << n%10;

    }
}

void print(long long n) { 
    if (n < 0) {
        std::cout << "-";
        n *= -1;
    }
    print(n, 0); 
}

And then in the main simply call print(n);

GBlodgett
  • 12,704
  • 4
  • 31
  • 45
  • While I like the idea of recursion, but your solution does not seem to work very well (yet) http://coliru.stacked-crooked.com/a/3a0823d54ea8c033 – Amadeus Sep 24 '19 at 01:35
  • I like this solution too. I took the idea and tweaked it a bit to get it to work (handle 0, handle negative numbers, put the comma in the right place), and passed in `std::ostream&` instead of hard-coded `std::cout`. Slick. – Eljay Sep 24 '19 at 03:27
1

Just to give you an idea how to solve this, I've maiden a simple implementation. Just keep in mind that is just a simple example:

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
  long long n = -1234567890;

  if ( n < 0 )
    cout << '-';

  n = abs(n);

  for (long long i = 1000000000000; i > 0; i /= 1000) {
    if ( n / i <= 0 ) continue;
    cout << n / i ;
    n = n - ( n / i) * i;
    if ( n > 0 )
       cout << ',';
  }

  return 0;
}

http://coliru.stacked-crooked.com/a/150f75db89c46e99

Amadeus
  • 10,199
  • 3
  • 25
  • 31
-2

A small template class comma_sep may be a solution, the usage may be as simple as:

cout << comma_sep<long long>(7497592752850).sep() << endl;

Which outputs:

7,497,592,752,850

Picked from here: https://github.com/arloan/libimsux/blob/main/comma_sep.hxx

template <class I = int, int maxdigits = 32>
class comma_sep 
    char buff[maxdigits + maxdigits / 3 + 2];
    char * p;
    I i;
    char sc;
public:
    comma_sep(I i, char c = ',') : p(buff), i(i), sc(c) {
        if (i < 0) {
            buff[0] = '-';
            *++p = '\0';
    }
    }

    const char * sep() {
        return _sep(std::abs(i));
    }
private:
    const char * _sep(I i) {
        I r = i % 1000;
       I n = i / 1000;
        if (n > 0) {
            _sep(n);
            p += sprintf(p, "%c%03d", sc, (int)r);
            *p = '\0';
        } else {
            p += sprintf(p, "%d", (int)r);
            *p = '\0';
        }
        return buff;
    }
};

The above class handles only integeral numbers, float/double numbers need to use a partial specialized version:

template<int maxd>
class comma_sep<double, maxd> {
    comma_sep<int64_t, maxd> _cs;
    char fs[64];
    double f;
public:
    const int max_frac = 12;
    comma_sep(double d, char c = ',') : _cs((int64_t)d, c) {
        double np;
        f = std::abs(modf(d, &np));
    }
    const char * sep(int frac = 3) {
        if (frac < 1 || frac > max_frac) {
            throw std::invalid_argument("factional part too too long or invalid");
        }
        auto p = _cs.sep();
        strcpy(fs, p);
        char fmt[8], tmp[max_frac+3];
        sprintf(fmt, "%%.%dlf", frac);
        sprintf(tmp, fmt, f);
        return strcat(fs, tmp + 1);
    }
};

The two above classes can be improved by adding type-traits like std::is_integral and/or std::is_floating_point, though.