Как С++ работает с 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 иначе?
Ответы
Ответ 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()