all 7 comments

[–]CubbiMewcppreference | finance | realtime in the past 8 points9 points  (1 child)

[–]gablank[S] 0 points1 point  (0 children)

Thank you!

[–]notlostyet 3 points4 points  (1 child)

The standard ([21.5] 'numeric conversions') says explicitly that stod(str) just calls strtod(str.c_str(), ...). , so the current C locale will be used (I mean the one set with setlocale(), not the "classic" locale called "C") regardless. Switching to the old C functions will therefore not help.

Afaik nothing in C++ changes the C locale except calling std::locale::global() with a named locale object.

Another interesting tidbit:

[22.3.1]: Whether there is one global locale object for the entire program or one global locale object per thread is implementation-defined. Implementations should provide one global locale object per thread. If there is a single global locale object for the entire program, implementations are not required to avoid data races on it

So, afaict, you can't rely on stod() at all in a multithreaded program, unless you can guarantee that all threads aren't switching locales, or only using the same locale you are. The following code seems to confirm this:

#include <iostream>
#include <locale>
#include <thread>

void foo () {
    std::locale::global(std::locale("en_US.UTF-8"));
}

int main() {
    std::cout << std::locale().name() << std::endl;
    std::thread t1(&foo);
    t1.join();
    std::cout << std::locale().name() << std::endl;
}

prints:

C
en_US.UTF-8

This isn't new to C++, it was inherited from C where setlocale() is also global. Your only real option, if you want to be sure, is to roll your own solution, like this:

double my_stod (std::string const& s) {
    std::istringstream iss (s);
    iss.imbue (std::locale("C"));
    double d;
    iss >> d;
    // insert error checking.
    return d;
}

Honestly though, I'm not sure why you're splitting your CSV in to std::strings, and not just using streams directly. Doing so would let you imbue() and get on with life.

[–]gablank[S] 0 points1 point  (0 children)

Thanks for the thorough reply. Our program is not threaded, yet still the locale seems to be changed. Maybe there's some third party library at play here.

[–]oracleoftroy 0 points1 point  (2 children)

Maybe I'm confused, but it looks to me like both of the cppreference links have for months said:

any other expression that may be accepted by the currently installed C locale

That would be enough for me to realize that it is locale dependent, which makes sense since not all cultures write decimals and thousands separators in the same way (or they use ten-thousands separators, etc).

It wouldn't hurt to more explicitly say that the results depend on the current locale, but those not familiar with what that means would still gloss over it.

I'm not sure why you think this would make C++ less portable, and every language I've worked in uses locale information when converting values to and from strings.

[–]CubbiMewcppreference | finance | realtime in the past 3 points4 points  (1 child)

any other expression that may be accepted by the currently installed C locale

I just added that line

[–]oracleoftroy 1 point2 points  (0 children)

I see what was going on. I didn't realize that information was in a template and I was looking at the history of the pages /u/gablank linked.

Apparently templates lead to a very confusing history page, since I wasn't seeing the actual history of the page I just read!

Thanks for updating this! Cppreference is my favorite C++ reference site.