Разобрать и преобразовать числа в деном?

В 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 исключение.

Итак, мой вопрос: Как преобразовать значение денорм хранится в строке?

3

Решение

Я не уверен, что понял проблему, но используя 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
2

Другие решения

Видимо, вы можете использовать strtod (или старше atof) интерфейс от cstdlib, Я сомневаюсь, это гарантировано или портативно.

0

Я не уверен, будет ли это иметь значение, но вы на самом деле печатаете:

(std::numeric_limits<double>::max_digits10 + 1) = 18 десятичные цифры.

например, 64-битный двойной стандарт IEEE-754 с высокой точностью "1.16" в научный нотации. Возможно это вводит некоторый ULP / округление, которое мешает преобразованию?

0

Проблема с денормалями и 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.

0
По вопросам рекламы [email protected]