is there a way to tweak std::stod() in order to increase the number of decimal digits in the (string to double) conversion and to force it to use the US locale?
I have a Qt application that can be run in both console or gui mode:
if (opt->getFlag( 'c' ) || opt->getFlag( "console" ) ){
ThreadManager modelMainThread;
modelMainThread.runFromConsole(inputFileName,scenarioName);
}
else {
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}
Within this application I have a string to double method that wraps the new C++11 stod:
double s2d ( const string &string_h) const {
try {
return stod(string_h);
} catch (...) {
if (string_h == "") return 0;
else {
cout << "error!" << endl;
}
}
return 0;
}
Odd enough, while in the console mode the string to double conversion expects a string with dot as decimal separator, in the gui mode it instead expects a string with comma.
Furthermore, as I was previously using istringstream:
istringstream totalSString( valueAsString );
totalSString >> valueAsDouble;
I noticed that stod truncates the resulting double to just 3 decimal digits, much less than istringstream.
So is there a way to increase the number of decimal digits and to force std::stod to use the US locale for the conversion ?
Thanks :-)
EDITED:
If I try this script:
// testing stod() ..
vector<string> numbers;
numbers.push_back("123.1234567890");
numbers.push_back("123.1234");
numbers.push_back("123,1234567890");
numbers.push_back("123,1234");
double outd;
for(uint i=0;i<numbers.size();i++){
try {
outd = stod(numbers[i]);
cout << "Conversion passed: " << numbers[i] << " - " << outd << endl;
} catch (...) {
cout << "Conversion DID NOT passed: " << numbers[i] << " - " <<endl;
}
}
I got these results:
"console" mode:
Conversion passed: 123.1234567890 - 123.123
Conversion passed: 123.1234 - 123.123
Conversion passed: 123,1234567890 - 123
Conversion passed: 123,1234 - 123
"gui" mode:
Conversion passed: 123.1234567890 - 123
Conversion passed: 123.1234 - 123
Conversion passed: 123,1234567890 - 123.123
Conversion passed: 123,1234 - 123.123
So clearly there is something influencing stod() behaviour !
std::stod
and its kin were designed to provide a simple, quick conversion from a string to a numeric type. (full disclosure: it's my design) So, no, no locales; what you see is what you get.
std::stod
is a somehow generic way of converting a std::string
to a double. If you want something more specific, you should implement it yourself.
For example:
double my_stod(const std::string &valueAsString) {
istringstream totalSString( valueAsString );
double valueAsDouble;
// maybe use some manipulators
totalSString >> valueAsDouble;
if(!totalSString)
throw std::runtime_error("Error converting to double");
return valueAsDouble;
}
std::stod
is defined in terms of std::strtod
, which is inherited from the C standard library. The C function strtod
works in terms of the C locale, accessible via the setlocale
function from the <locale.h>
header.
In C++, the C locale is still accessible via std::setlocale
function in the <clocale>
header, and it does influence both std::strtod
and std::stod
.
Qt's QApplication
uses std::setlocale
to set the user-chosen locale. Thus whenever you use a C-locale-dependent function in a GUI Qt application, you'll have locale-dependent radix point.
Now, to force a particular locale for numbers, you can use std::setlocale
as follows. Note though, that this can break multithreaded apps, since C locale is a thread-global state. The example below will set program's locale temporarily to LC_NUMERIC=C
, and restore the setting after calling std::stod
.
#include <iostream>
#include <clocale>
#include <vector>
#include <string>
void test()
{
for(auto s : {"123.1234567890",
"123.1234",
"123,1234567890",
"123,1234"})
{
// Save locale setting
const auto oldLocale=std::setlocale(LC_NUMERIC,nullptr);
// Force '.' as the radix point. If you comment this out,
// you'll get output similar to the OP's GUI mode sample
std::setlocale(LC_NUMERIC,"C");
try
{
const auto outd=std::stod(s);
std::cout << "Conversion succeeded: " << s << " => "
<< outd << '\n';
}
catch (...)
{
std::cout << "Conversion FAILED : " << s << " => ???\n";
}
// Restore locale setting
std::setlocale(LC_NUMERIC,oldLocale);
}
}
#include <QApplication>
int main(int argc, char** argv)
{
std::cout << "Test in normal console mode\n";
test();
QApplication app(argc, argv);
std::cout << "Test in GUI mode\n";
test();
}
Output:
Test in normal console mode
Conversion succeeded: 123.1234567890 => 123.123
Conversion succeeded: 123.1234 => 123.123
Conversion succeeded: 123,1234567890 => 123
Conversion succeeded: 123,1234 => 123
Test in GUI mode
Conversion succeeded: 123.1234567890 => 123.123
Conversion succeeded: 123.1234 => 123.123
Conversion succeeded: 123,1234567890 => 123
Conversion succeeded: 123,1234 => 123