
Each C ++ developer sooner or later encounters the features of converting a fractional number from a string representation (std :: string) into a directly floating point number (float) associated with the locale installed. As a rule, a problem arises with a different representation of the separator of the integer and fractional parts in the decimal notation of the number ("," or ".").
This article focuses on the duality of C ++ locales. If you are wondering why the conversion of the same std :: string ("0.1") using std :: stof () and std :: istringstream to float can lead to different results, please under the cat.
Problem
As in many Habr's articles, everything started with an error in the code, a fragment of which can be reduced to the following:
')
float valf = std::stof(str);
“It’s a matter of locale,” I think, so for debugging purposes, before converting, I’ll append a line to display the real separator of the integer and fractional parts on the screen, expecting to see there ",":
std::locale lcl;
A few words about the submitted codeFor the sake of a beautiful code, it is worth noting that it would be more appropriate to add a check for the existence of a facet:
std::locale lcl; if (std::has_facet<std::numpunct<char>>(lcl)) {
More details about working with facets and locales in C ++ can be found here:
on Habré ,
in the documentation .
It turns out that the locale is set correctly, and the string "0.1" should be converted correctly. Check the conversion via std :: istringstream:
float valf = std::stof(str);
We get that conversion through std :: istringstream works as expected, while std :: stof () returns an invalid value.
The essence
In C ++, there are two global locales:
At the same time, changing the global locale using the std :: locale :: global () function changes both the STL locale and the C-library locale, while the setlocale () function
only affects the second one.
Thus, the mismatch is possible:
auto * le = localeconv(); std::cout << le->decimal_point << std::endl;
The catch is that the function from C ++ 11 std :: stof () (like std :: stod ())
is based on the function strtod () (or wcstod ()) from library C, which, in turn, focuses on the C-library locale. It turns out that the behavior of the C ++ function is based on the C-library locale, and not on the STL locale, as expected.
Conclusion
C ++ STL functions can use C-library functions in their work, which can lead to unexpected results, in particular, in case of a mismatch between the global STL locales and the C-library. Need to keep that in mind.
In my particular case, the Qt library's QCoreApplication class was
“guilty” under * nix, which, when initialized, causes setlocale (), thereby leading to a possible mismatch of the described locales.
PS As many will rightly say, the Qt library has its own means of converting a string to a number, just like its own global locale (QLocale). The described situation arose when integrating code from a project using only STL into a Qt project.