В C ++ мы можем без проблем хранить числа денорм в переменных:
double x = std::numeric_limits<double>::denorm_min();
Затем мы можем напечатать эту переменную без проблем:
std::cout<<std::setprecision(std::numeric_limits<double>::max_digits10)
std::cout<<std::scientific;
std::cout<<x;
std::cout<<std::endl;
И это напечатает:
4.94065645841246544e-324
Но проблема возникает, когда кто-то пытается разобрать этот номер. Представьте, что это число хранится в файле и читается как строка. Проблема в том, что:
std::string str = "4.94065645841246544e-324";
double x = std::stod(str);
бросит std::out_of_range
исключение.
Итак, мой вопрос: Как преобразовать значение денорм хранится в строке?
Я не уверен, что понял проблему, но используя std::istringstream
как это:
std::string str = "4.94065645841246544e-324";
double x;
std::istringstream iss(str);
iss >> x;
std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);
std::cout << std::scientific;
std::cout << x << std::endl;
…дает мне:
4.94065645841246544e-324
Видимо, вы можете использовать strtod
(или старше atof
) интерфейс от cstdlib
, Я сомневаюсь, это гарантировано или портативно.
Я не уверен, будет ли это иметь значение, но вы на самом деле печатаете:
(std::numeric_limits<double>::max_digits10 + 1) = 18
десятичные цифры.
например, 64-битный двойной стандарт IEEE-754 с высокой точностью "1.16"
в научный нотации. Возможно это вводит некоторый ULP / округление, которое мешает преобразованию?
Проблема с денормалями и std::stod
является то, что последний определяется с точки зрения std::strtod
, который может установить errno=ERANGE
on underflow (определяется реализацией, будет ли это делать, а в glibc -). Как напомнили разработчиками gcc, в таком случае std::stod
определяется стандартом бросить std::out_of_range
,
Таким образом, ваш правильный обходной путь должен использовать std::strtod
напрямую, игнорируя ERANGE
когда возвращаемое значение является конечным и ненулевым, как здесь:
double stringToDouble(const char* str, std::size_t* pos=nullptr)
{
errno=0;
char* end;
const auto x=std::strtod(str, &end);
if(errno==ERANGE)
{
// Ignore it for denormals
if(x!=0 && x>-HUGE_VAL && x<HUGE_VAL)
return x;
throw std::out_of_range("strtod: ERANGE");
}
else if(errno)
throw std::invalid_argument("strtod failed");
if(pos)
*pos=end-str;
return x;
}
Обратите внимание, что в отличие от std::istringstream
подход, предложенный в другой ответ, это будет работать и для hexfloats.