stringstream ss(str); // Конструирование из строки типа string
double d = 0;
ss >> d;
if (ss.fail) {
string s = "Невозможно отформатировать ";
s += str;
s += "
как число!";
throw (s);
}
return (d);
Наиболее важной частью здесь является то, что все, что требуется сделать, — это использовать для чтения из строкового потока в
double
оператор сдвига вправо (
>>
), как это делается при чтении из
cin
.
Ну, это не совсем все, что требуется сделать. Если в
stringstream
записано значение, которое не может быть записано в переменную в правой части оператора
>>
, то для потока будет выставлен бит
fail
. Этот бит можно проверить с помощью функции-члена
fail
(на самом деле это функция-член
basic_ios
, который является родительским классом для
stringstream
). Кроме того, переменная справа от оператора
>>
в случае ошибки значения не меняет.
Однако с целью обобщения можно избежать написания отдельных версий
sciToDub
для типов
int
,
float
,
double
и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.
template<typename T>
T strToNum(const string& str) {
stringstream ss(str);
T tmp;
ss >> tmp;
if (ss.fail) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (tmp);
}
Теперь, чтобы преобразовать
string
в числовой тип, можно сделать так.
double d = strToNum<double>("7.0");
float f = strToNum<float>("7.0");
int i = strToNum<int>("7.0");
Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.
Смотри также
Рецепт 3.2.
3.6. Преобразования между числовыми типами
Проблема
Имеется число одного типа и требуется преобразовать его в другой, как
int
в
short
или наоборот, но при этом необходимо перехватывать все ошибки переполнения (overflow) или потери значимости (underflow), возникающие при работе программы.
Решение
Используйте шаблон класса
numeric_cast
Boost. Он выполняет проверки, которые при переполнениях переменной, принимающей значение, или других ошибках выбрасывают
исключение типа
bad_numeric_cast
. Пример 3.8 показывает, как это выполняется.
Пример 3.8. Безопасное преобразование чисел
#include <iostream>
#include <boost/cast.hpp>
using namespace std;
using boost::numeric_cast;
using boost::bad_numeric_cast;
int main {
// Целые типы
try {
int i = 32767;
short s = numeric_cast<short>(i);
cout << "s = " << s << endl;
i++; // Теперь i выходит за диапазон (если sizeof(short) равен 2)
s = numeric__cast<short>(i);
} catch (bad_numeric_cast& e) {
cerr << e.what << endl;
}
try {
int i = 300;
unsigned int ui = numeric_cast<unsigned int>(i);
cout << ui << endl; // Прекрасно
i *= -1;
ui = numeric_cast<unsigned int>(i); // i отрицателен!
} catch (bad_numeric_cast& e) {
cerr << e.what << endl;
}
try {
double d = 3.14.
int i = numeric_cast<int>(d);
i = numeric_cast<int>(d); // Это отрезает 0.14!
cout << i << endl; // i = 3
} catch (bad_numeric_cast& e) {
cerr << e.what << endl;
}
}
Обсуждение
Вы, вероятно, знаете, что базовые типы C++ имеют различные размеры. Стандарт C++ содержит жесткие указания по относительному размеру типов:
int
всегда не короче, чем
short int
, но он не указывает абсолютных размеров. Это означает, что если взять
long int
и попытаться записать его значение в
short
или попытаться поместить
int
в
unsigned int
, то информация о значении переменной-источника, такая как знак или даже часть числового значения, может быть потеряна.
Только знания, что это может привести к проблемам, не достаточно. Вы можете быть ограничены жесткими требованиями по объему и не захотите использовать четыре байта для
long
, когда можно обойтись двумя байтами для
short
(если ваша платформа на самом деле использует такие размеры, что очень распространено, но не гарантируется). Из-за ограничений по объему может возникнуть желание попробовать хранить значения в наименьших возможных типах. Если вы любите приключения, но вам нужна страховка, для перехвата потерь данных при работе программы используйте