Есть хороший способ читать double
из строки относительно переполнения, то есть конвертировать "1e500" -> +inf
?
Способы чтения из строки мне известны:
::std::stringstream
— возвращает мусор при переполнении;::boost::lexical_cast
— брошенный bad_lixecal_cast без достойной информации о конвертируемом числе::std::strtod
от cstdlib
— afaik это единственный, который сообщает о переполнении (возвращая HUGE_VAL
и настройка errno
в ERANGE
), но использовать его тихо неудобноВ частности, мне нужен метод, чтобы надежно преобразовать строку в число (double
), например
"1e100" -> 1e100
"1e300" -> 1e300
"1e309" -> +inf // handling overflow as ieee-754 'inf'
РЕДАКТИРОВАТЬ:
Я на самом деле использую последний подход, вот код:
double stringToDouble(char const *str)
{
double result = ::std::strtod(str, 0);
if (ERANGE == errno)
{
if (HUGE_VAL == result)
{
result = INFINITY;
}
else if (-HUGE_VAL == result)
{
result = -INFINITY;
}
}
return result;
}
Я был довольно удивлен, что stringstream
не справляется с переполнением Но это на самом деле возвращает какой-то другой double
значение (не связано с тем, что читается), и сообщает только через stream :: fail ().
Но все же я ищу немного C ++ — способ чтения чисел.
Что ж, ваш нынешний способ работы в C намного эффективнее, чем я собираюсь предложить, но, поскольку вы запрашиваете метод C ++, вот один из них — определение объекта, похожего на манипулятор, для защиты вас от переполнений:
#include "stdafx.h"#include <iostream>
#include <string>
#include <limits>
#include <algorithm>
using namespace std;
struct OverflowProtect
{
} limitdouble;
struct DoubleOverflowException : public std::exception
{
};
double stringToDouble(char const *str)
{
double result = ::std::strtod(str, 0);
if (ERANGE == errno)
{
if (HUGE_VAL == result)
{
throw DoubleOverflowException(); // throw whatever exception you want here
}
else if (-HUGE_VAL == result)
{
throw DoubleOverflowException(); // throw whatever exception you want here
}
}
return result;
}
istream & operator >> (istream & aIn, const OverflowProtect & aManip)
{
string number;
aIn >> number;
stringToDouble(number.c_str());
for_each(number.rbegin(), number.rend(), [&aIn](char c){aIn.putback(c);});
return aIn;
}
int _tmain(int argc, _TCHAR* argv[])
{
double nr;
try
{
cin >> limitdouble >> nr;
}
catch ( DoubleOverflowException & e )
{
// handle overflow exception thrown by limitdouble
e;
}
return 0;
}
Не самый эффективный способ, особенно operator>>
реализация, но, несомненно, C ++ — как, и очень весело.
Я уверен, что улучшения могут быть сделаны, я просто иллюстрирую идею.
Других решений пока нет …