Явная спецификация представления двойной точности, используемого компилятором

Недавно я столкнулся с проблемой, что visual-c++ не соответствует IEEE 754, но вместо этого использует субнормальное представление. То есть числа с плавающей запятой двойной точности в нем не имеют обычного представления из 1 знакового бита, 11 битов экспоненты и 52 явно сохраненных значащих десятичных битов, см. ниже.

Однако, поскольку gcc и clang совместимы, а единообразное межплатформенное поведение крайне желательно, я хотел бы знать, можно ли заставить visual-c++ использовать нормальное представление. В качестве альтернативы использование gcc и clang субнормального представления, конечно, также решит проблему.

Проблема различных двойных представлений может быть воспроизведена в visual-c++, gcc и clang, используя следующий код:

#include <iostream>
#include <string>

int main()
{
    try {
        std::stod("8.0975711886543594e-324");
        std::cout << "Subnormal representation.";
    } catch (std::exception& e) {
        std::cout << "Normal representation.";
    }
    return 0;
}

Возможна ли вообще спецификация представления для создания согласованного поведения во всех трех случаях?

Изменить: Как указал geza, это проблема в различных реализациях std::stod, что затем вызовет вопрос, есть ли способ заставить std::stod вести себя последовательно, без необходимости реализовывать для него отдельную оболочку.


person abcalphabet    schedule 19.11.2018    source источник
comment
Здесь разница заключается в std::stod, не так ли? GCC и clang выбрасывают out_of_range для субнормальных чисел (что странно, если вы спросите меня), а msvc — нет. Я даже не понимаю, почему они выбрасывают out_of_range для небольшого числа, которое обрезается до нуля. Это определенно не out_of_range. Для этого случая должно быть отдельное исключение.   -  person geza    schedule 19.11.2018
comment
Релевантно: stackoverflow.com/questions/48086830/   -  person geza    schedule 19.11.2018
comment
std::stod плохо спроектирован. strtod может сообщить, означает ли ERANGE переполнение или недополнение. std::stod не может.   -  person geza    schedule 19.11.2018
comment
@geza Спасибо, отредактировал сообщение, чтобы отразить, что это std::stod, может быть, есть какой-то стандартный способ обойти дизайн.   -  person abcalphabet    schedule 19.11.2018
comment
Да, используйте strtod, у которого нет этого ограничения. Он ведет себя не так, как задокументировано для субнормальных чисел: он возвращает субнормальное значение (поэтому, вопреки документу, он возвращает ненулевое значение, но устанавливает errno=ERANGE). Что хорошо для нас, потому что вы можете получить номер, который вы хотите.   -  person geza    schedule 19.11.2018
comment
@geza Ага, отлично справился, все, что мне было нужно, это std::strtod(cstr, nullptr), спасибо! Если вы хотите превратить это в ответ, я был бы рад принять его   -  person abcalphabet    schedule 19.11.2018


Ответы (1)


К сожалению, std::stod плохо спроектирован, потому что невозможно определить, что вызвало исключение std::out_of_range.

Я бы предложил вам использовать strtod вместо этого. Хотя в стандарте не указано, что эта функция должна делать для субнормальных чисел, обычно она ведет себя хорошо для субнормальных чисел (это означает, что она возвращает субнормальные числа). Преимущество этой функции в том, что она возвращает осмысленный результат для ситуаций, выходящих за пределы диапазона, что позволяет определить причину выхода за пределы диапазона.

Если вы хотите обрабатывать ситуации вне диапазона, вам нужно проверить errno для ERANGE. Обратите внимание, что если результатом является субнормальное/нулевое число, то, возможно, errno будет установлено на ERANGE, что вам следует игнорировать (вы можете проверить это с помощью fpclassify).

Итак, логика примерно такая:

double r = strtod(string, &end);
// here, check for end to know about invalid strings

if (errno==ERANGE) { // out-of-range (overflow, underflow)
    int c = fpclassify(r);
    if (c!=FP_SUBNORMAL&&c!=FP_ZERO) { // let's filter out underflow cases
        // "real" out of range handling here, just overflow
    }
}
person geza    schedule 19.11.2018