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
In order to avoid threading issues when setting the locale and using strtod
, I would use istringstream
:
#include <optional>
#include <sstream>
[[nodiscard]] std::optional<double>
toDouble( const std::string& stringToConvert)
{
std::istringstream streamToConvert( stringToConvert );
streamToConvert.imbue( std::locale( "C" ) );
double result = 0.0;
streamToConvert >> result;
if ( streamToConvert.fail() || streamToConvert.bad() || !streamToConvert.eof() ) {
return std::nullopt;
}
return result;
}
If you really don't want to use istringstream
, you could try to call std::num_get
, which is what istringstream
calls internally.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.