Search code examples
c++localenumber-formattingstringstreamseparator

Comma Separate Number Without stringstream


So typically if I wanted to insert locale appropriate separators in some number, foo, I'd do something like this:

ostringstream out;

out.imbue(locale("en-US"));
out << foo;

Then I could just use out.str() as the separated string: http://coliru.stacked-crooked.com/a/054e927de25b5ad0

Unfortunately I've been asked not to use stringstreams in my current project. Is there any other way I can accomplish this? Ideally a locale dependent way?


Solution

  • So this answer is the C++ distillation of Jerry Coffin's answer to this question: Cross Platform Support for sprintf's Format '-Flag

    template <typename T>
    enable_if_t<is_integral_v<remove_reference_t<T>>, string> poscommafmt(const T N, const numpunct<char>& fmt_info) {
        const auto group = fmt_info.grouping();
        auto posn = cbegin(group);
        auto divisor = static_cast<T>(pow(10.0F, static_cast<int>(*posn)));
        auto quotient = div(N, divisor);
        auto result = to_string(quotient.rem);
    
        while(quotient.quot > 0) {
            if(next(posn) != cend(group)) {
                divisor = static_cast<T>(pow(10.0F, static_cast<int>(*++posn)));
            }
            quotient = div(quotient.quot, divisor);
            result = to_string(quotient.rem) + fmt_info.thousands_sep() + result;
        }
        return result;
    }
    
    template <typename T>
    enable_if_t<is_integral_v<remove_reference_t<T>>, string> commafmt(const T N, const numpunct<char>& fmt_info) {
        return N < 0 ? '-' + poscommafmt(-N, fmt_info) : poscommafmt(N, fmt_info);
    }
    

    Naturally this suffers from the identical 2's compliment negation issue.

    This certainly benefits from C++'s string memory management, but also from the ability to pass in a specific numpunct<char> which need not be the current locale. For example whether or not cout.getloc() == locale("en-US") you can call: commafmt(foo, use_facet<numpunct<char>>(locale("en-US")))

    Live Example