Я пишу программу, которая использует очень длинную рекурсию (около 50 000) и несколько очень больших векторов (также 50 000 длиной типа double) для хранения результатов каждой рекурсии перед их усреднением. В конце программы я ожидаю получить вывод числа.
Тем не менее, некоторые из результатов, которые я получил, были «нан». Загадочная вещь в том, что если я уменьшу количество рекурсий, программа будет работать нормально. Я предполагаю, что это может быть связано с размером вектора. Поэтому мой вопрос: если вы получите переполнение очень длинного вектора (или, скажем, массива), каков будет эффект? Получите ли вы «нан», как в моем случае?
Еще одна загадочная вещь в моей программе заключается в том, что я пробовал еще большие рекурсии (100 000), но результат был нормальным. Но когда я изменил значение параметра, чтобы каждое число, хранящееся в векторе, становилось больше (хотя они все еще имеют тип double), вывод становится «nan». Будет ли максимальная емкость вектора зависеть от размера числа, которое он хранит?
Вы не сказали нам, какова ваша рекурсия, но довольно просто генерировать NaN с длинной последовательностью операций, если вы используете квадратный корень, Pow, обратный синус или обратный косинус.
Предположим, что ваш расчет дает количество, назовите его x
, это должно быть синусом некоторого угла θ, и предположим, что лежащая в основе математика диктует, что x
всегда должно быть между -1 и 1 включительно. Вы вычисляете θ, беря обратный синус x
,
Вот проблема: арифметика, выполненная на компьютере, является лишь приближением арифметики действительных чисел. Сложение и умножение с числами с плавающей запятой IEEE не являются транзитивными. Вы можете получить значение 1.0000000000000002 для x
вместо 1. Возьмите обратный синус этого значения, и вы получите NaN.
Стандартный трюк заключается в защите от тех промахов, которые возникают в результате числовых ошибок. Не используйте встроенный asin
, acos
, sqrt
, а также pow
, Используйте обертки, которые защищают от таких вещей, как asin(1.0000000000000002)
а также sqrt(-1e-16)
, Сделайте первый пи / 2, а не NaN, и сделайте второй ноль. Это, по общему признанию, клочок, и это может привести к неприятностям. Что если проблема в том, что ваши расчеты сформулированы неправильно? Допустимо рассматривать 1.0000000000000002 как 1, но лучше не рассматривать значение 100, как если бы оно было 1. Значение 100 для вашего asin
Обертку лучше всего обрабатывать, выбрасывая исключение, а не обрезая до 1.
Есть еще одна проблема с бесконечностями и NaN: они размножаются. Inf или NaN в одном вычислении быстро становится Inf или NaN в сотнях, а затем в тысячах значений. Обычно я заставляю механизм с плавающей запятой вызывать исключение с плавающей запятой при получении Inf или NaN вместо продолжения. (Обратите внимание: исключения с плавающей точкой не являются исключениями C ++.) Когда вы делаете это, ваша программа будет бомбить, если у вас нет обработчика сигнала на месте. Это не обязательно плохо. Вы можете запустить программу в отладчике и найти, где именно возникла проблема. Без этих исключений с плавающей точкой очень трудно найти источник проблемы.
Зависит от точного характера ваших вычислений. Если вы просто сложите числа, которые не NaN, результат также не должен быть NaN. Это может быть + бесконечность, хотя.
Но ты будут получить NaN, например, какая-то часть ваших вычислений дает + бесконечность, другая — бесконечность, и вы позже добавите эти два результата.
Предполагая, что ваша архитектура соответствует IEEE 754, это http://en.wikipedia.org/wiki/NaN#Creation рассказывает ситуации, в которых арифметические операции возвращают NaN.