Подтвердить что ты не робот

Как С++ работает с NAN? Существует ли стандартный способ или зависит от компилятора?

В программе, которая должна обрабатывать функцию sin (x)/x, я столкнулся с проблемой NAN, я упростил проблему в следующем коде:

#include <iostream>
#include <cmath>

int main()
{
    std::cout.precision(15);

    //This line compiles and run in g++, but does not compile in Visual Studio 2013
    std::cout << 0.0/0.0 << std::endl;

    //This line compiles and run in both g++ and VS2013
    std::cout << std::sin(0.0)/0.0 << std::endl;

    return 0;
}

В g++ вывод: -nan -nan, в VS2013 вывод: -1.IND, потому что первая строка не компилируется, поэтому я прокомментировал ее.

Мои вопросы:

  • Что означает этот "-1.IND"?

  • Кажется, что процесс NAN зависит от компилятора, должен ли он быть стандартизован в С++? Почему?

  • Я использовал этот хак для решения этой проблемы:

    double sinc(double x)
    {
        if(x == 0.0)
            return 1.0;
        return std::sin(x)/x;
    }
    

Правильно ли это?

EDIT: другой вопрос, 4. почему VS2013 имеет дело с 0.0/0.0 и sin (0.0)/0.0 иначе?

4b9b3361

Ответ 1

Есть похожие вопросы, на которые вы ответили SO:

1. Что означает этот "-1.IND"?

См. Что делать 1. # INF00, -1. # IND00 и -1. # IND mean?

2. Кажется, что процесс NAN зависит от компилятора, должен ли он быть стандартизован в С++? Почему?

Смотрите Несколько вещей о делении на ноль в C (он говорит C, но он говорит о С++)

3. Я использовал этот хак для решения этой проблемы:

double sinc(double x) {

    if(x == 0.0)
        return 1.0;
    return std::sin(x)/x; 
}

Правильно ли это?

Да, эта реализация функции sinc будет работать и (благодаря @MSalters для комментария) математически корректна; однако имейте в виду, что, хотя это будет работать для этого случая, не делайте привычки сравнивать типы double с ==.

Ответ 2

Чтобы добавить ответ для (4), sin(x) - это функция времени выполнения, и, таким образом, sin(0.0)/0.0 обрабатывается как выражение, которое оценивается во время выполнения. OTOH, 0.0/0.0 полностью обрабатывается компилятором, поэтому он выявляет проблему. Деталь реализации вашей версии Visual Studio, а не то, на что вы можете рассчитывать.

Ответ 3

Полное соответствие стандарту с плавающей точкой IEEE является необязательным в стандартах C и С++. Это связано с тем, что по причинам, которые мне никогда не были понятны, дизайнеры CPU ненавижу стандарт с плавающей точкой IEEE. Я не знаю ни одного процессора, который бы правильно и эффективно реализовал всю спецификацию. Насколько я знаю, лучшее, что я могу сказать, это процессор, который быстро и корректно работает для нормальных конечных чисел, но страдает от нескольких порядков замедления, если вы хотите работать с денормалами, бесконечностью и NaN.)

Стандарты C и С++ написаны людьми-компиляторами. Компиляторы хотят, чтобы иметь возможность генерировать машинный код, который быстро работает на реальных процессорах, и они знают, что реальные CPU срезают углы с плавающей точкой IEEE, поэтому они не делают полное соответствие требованиям с плавающей точкой IEEE требованиям языка.

Ответ 4

  • Компиляция выражения double res = 0.0 / 0.0 терпит неудачу, потому что компилятор пытается оптимизировать код, который определяет "недопустимое" выражение. Он не может оптимизировать выражение типа sin(x) / 0.0, потому что он не может упростить и "оптимизировать" код, подобный этому ".
  • Отображение специальных значений типа double зависит от платформы. Но представление зависит от архитектуры. Чтобы проверить это, вы можете запустить эту функцию на разных архитектурах: template<typename T> void inspect(T v1, T v2) { T i_val = v1; i_val /= v2; size_t len = sizeof(T); int* bit_repr = (int*)(&i_val); for (int i = 0; i < (len / sizeof(int)); i ++) { std::bitset<sizeof(int) * 8> bs(*(bit_repr + i)); std::cout << bs; } std::cout << std::endl; }

Вызов предыдущей функции со следующими аргументами:

inspect<double>(0.0, 0.0);
inspect<double>(1.0, 0.0);
inspect<double>(-1.0, 0.0);

Ответ 5

Это может быть полезно: С++ 11 (так VS2013) имеет некоторую функцию, чтобы понять, является ли данное число NAN или конечным. Используйте std:: isnan() или std:: isfinite()