すべてのC ++開発者は、遅かれ早かれ、小数を文字列表現(std :: string)からインストールされたロケールに直接関連付けられた浮動小数点数(float)に変換する特性に直面します。 通常、数値の10進表記( "、"、または "。")で整数部分と小数部分の区切り文字が異なる場合に問題が発生します。
この記事では、C ++ロケールの二重性に焦点を当てます。 同じstd :: string( "0.1")をstd :: stof()およびstd :: istringstreamをfloatに変換すると異なる結果になるのはなぜかと思ったら、catを使用してください。
問題
多くのHabrの記事のように、それはすべてコードのエラーから始まりましたが、その断片は次のように減らすことができます。
float valf = std::stof(str); // str = std::string("0.1") std::cout << valf << std::endl; // 0, 0.1
「それはロケールの問題です」と思うので、デバッグの目的で、変換する前に整数部分と小数部分の現在のセパレータの画面に出力行を追加し、「、」が表示されることを期待します。
std::locale lcl; // const auto & facet = std::use_facet<std::numpunct<char>>(lcl); std::cout << facet.decimal_point() << std::endl; // !
提出されたコードについて一言
美しいコードのために、ファセットの存在のチェックを追加する方が正しいことは注目に値します。
C ++でのファセットとロケールの操作の詳細については、 Habr のドキュメントを参照してください 。
std::locale lcl; if (std::has_facet<std::numpunct<char>>(lcl)) { //... }
C ++でのファセットとロケールの操作の詳細については、 Habr のドキュメントを参照してください 。
ロケールが正しく設定されており、文字列「0.1」が正しく変換される必要があることがわかりました。 std :: istringstream:を介して変換を確認します。
float valf = std::stof(str); // str = std::string("0.1") std::cout << valf << std::endl; // 0, 0.1 std::istringstream iss(str); iss >> valf; std::cout << valf << std::endl; // 0.1, !
std :: istringstreamによる変換は期待どおりに機能し、std :: stof()は無効な値を返します。
エッセンス
C ++には、2つのグローバルロケールがあります。
- STLロケール。 ファセットおよびクラスstd :: locale (#include <locale>)を介して可能な作業。
- Cライブラリロケール。setlocale()およびlocaleconv() (#include <clocale>)関数で使用できます。
同時に、std :: locale :: global()関数を使用してグローバルロケールを変更すると、STLロケールとCライブラリロケールの両方が変更されますが、setlocale()関数は2番目のロケールのみに影響します。
したがって、不一致が発生する可能性があります。
auto * le = localeconv(); std::cout << le->decimal_point << std::endl; // std::locale lcl; // const auto & facet = std::use_facet<std::numpunct<char>>(lcl); std::cout << facet.decimal_point() << std::endl; // !
キャッチは、C ++ 11 std :: stof()(std :: stod()と同様)の関数が、Cライブラリのstrtod()(またはwcstod())関数に基づいていることです。 Cライブラリロケールに焦点を当てています。 C ++関数の動作は、予想どおり、STLロケールではなく、Cライブラリロケールに依存していることがわかりました。
おわりに
作業中のC ++ STL関数はCライブラリの関数を使用できます。これにより、特にSTLとCライブラリのグローバルロケールが一致しない場合に予期しない結果が生じる可能性があります。 これに留意する必要があります。
私の特定のケースでは、* nixの下で、QtライブラリのQCoreApplicationクラスは「非難」され 、初期化されるとsetlocale()を呼び出すため、記述されたロケールの不一致が発生する可能性があります。
PS多くの人が正しく指摘しているように、Qtライブラリには文字列を数値に変換する独自の手段と、独自のグローバルロケール(QLocale)があります。 説明した状況は、STLのみを使用するプロジェクトのコードをQtプロジェクトに統合するときに発生しました。