2016-04-22 2 views
0

Я столкнулся с вопросом при вычислении суммы двойного. Когда я устанавливаю итерацию на 100000, функция Asian_call_MC все равно возвращает число. Однако, когда я устанавливаю итерацию около 500000 и выше, она начинает возвращать 1. # INF. Может ли кто-нибудь сказать мне, почему это происходит и как его решить? Я использую visual studio 2013 для написания кода на C++.#inf C++ visual studio

double Avg_Price(double init_p, double impl_vol, double drift, int step, double deltasqrt) 
{ 
//Calculate the average price of one sample path 
//delta = T/ step 
//drift = (risk_free - div_y - impl_vol*impl_vol/2)*(T/step) 
double Sa = 0.0; 
double St = init_p; 

for (int i = 0; i < step; i++) 
{ 
    St = St*exp(drift + impl_vol*deltasqrt*normal_gen()); 
    //Sa = Sa * i/(i + 1) + St/(i + 1); 
    Sa += St; 
} 
Sa = Sa/double(step); 
return Sa; 
} 


double Asian_call_MC(double strike_p, double T, double init_p, double impl_vol, double risk_free, double div_y, int iter, int step) 
{ 
//Calculate constants in advance to reduce computation time 
double drift = (risk_free - div_y - impl_vol*impl_vol/2)*double(T/step); 
double deltasqrt = sqrt(double(T/step)); 


//Generate x1, average x and y 
double cur_p = Avg_Price(init_p,impl_vol,drift,step,deltasqrt); 
double pay_o=0.0; 
double x = max(cur_p - strike_p,0.0); 
//double y = pow(x, 2.0); 


//Generate x2 to xn 
for (int i = 0; i < iter; i++) 
{ 
    cur_p = Avg_Price(init_p, impl_vol, drift, step, deltasqrt); 
    x = max(cur_p - strike_p,0.0); 
    //double q = double(i)/double(i + 1); 
    //pay_o = pay_o *i/(i+1) + x/(i + 1); 
    pay_o += x; 
    //y = (1 - (1/(i + 1)))*y + x*x/(i + 1); 
} 
//pay_o = pay_o/double(iter); 
//stdev = sqrt((y - pow(pay_o , 2))/(iter - 1)); 
//return pay_o*exp(-risk_free*T) ; 
return pay_o; 
} 
+0

Что возвращает 'normal_gen'? Какой у вас номер с 100000 итерациями? – 1201ProgramAlarm

+0

normal_gen генерирует нормально распределенное число со средним значением 0 и дисперсией 1. Сумма составляет около 800000, когда я устанавливаю итерацию на 100000. – zhnzhang61

+0

Таким образом, она может возвращать «большое» число, а затем и не ограничена. В какой-то момент одна из ваших итераций получает там достаточно большое значение, которое в сочетании со значениями для дрейфа, impl_vol и deltasqrt приводит к переполнению (любое значение около 710). – 1201ProgramAlarm

ответ

0

Похоже, вы хотите вычислить средние значения. То, как большинство людей учится вычислять среднее, состоит в том, чтобы суммировать все значения, а затем разделить сумму на количество значений, которые способствовали сумме.

Этот метод имеет несколько проблем, связанных с ним - например, добавление многих значений вместе может дать сумму, которая слишком велика для переменной, удерживающей ее.

Часто используется другая техника, которая накапливает «бегущее» среднее значение вместо суммы. Среднее значение рабочего времени всегда является средним значением для всех уже накопленных образцов, поэтому оно никогда не врывается в бесконечное значение переполнения (с плавающей точкой) (за исключением случаев, когда один из накопленных выборок был бесконечным).

В приведенном ниже примере показано, как вычислять текущее среднее значение. Он также вычисляет сумму и показывает, как сумма/счет сравняется с текущим средним значением (чтобы показать, что они одинаковы - я не позволял ему работать достаточно долго, чтобы переполнить сумму).

В примере используется C-библиотека rand() для демонстрационных целей - мне просто нужно что-то для вычисления средних значений.

#include <cstdlib> 
#include <ctime> 
#include <iostream> 
#include <iomanip> 

int main() { 
     srand(static_cast<unsigned>(time(0))); 

     double count = 0; 
     double running_mean = 0; 
     double sum = 0; 

     auto start = time(0); 
     auto end = start + 5; 
     while(time(0) < end) { 
      double sample = rand(); 
      count += 1; 
      running_mean += (sample - running_mean)/count; 
      sum += sample; 
     } 

     std::cout << std::setprecision(12); 
     std::cout << "running mean:" << running_mean << " count:" << count << '\n'; 
     double sum_mean = sum/count; 
     std::cout << "sum:" << sum << " sum/count:" << sum_mean << '\n'; 
} 

Edit: Он уже попробовал это - техник появился в закомментированных линиях, которые я пропустил в коде OP в

В отличии от вычисления среднего значения пути накопления грандиозной суммы, бег средняя технология не может просто переполняться в какой-то момент.Поэтому, зная, что он уже пробовал это и что это не помогло проблеме, вероятная причина становится тем, что одним из терминов итерации является сам INF. Как только будет добавлен один член INF, накопленная сумма или среднее значение станет INF и останется INF.

Наиболее вероятный раздел кода: normal_gen(), используемый внутри аргумента для вызова функции exp. Имя normal_gen() звучит как источник обычно распределенных случайных значений. В обычной реализации используется преобразование Box-Muller, которое не может вывести значения из примерно 7 стандартных отклонений от среднего значения. Поэтому, если генератор Box-Muller вызывает INF, это, вероятно, произойдет в меньшем количестве итераций, чем указано. Однако более продвинутые генераторы могут создавать более экстремальные значения - в идеале нормальное распределение имеет отличную от нуля вероятность получения любого конечного действительного значения.

Если случайная большая нормальная выборка является причиной проблемы, ее корреляция с увеличенным числом итераций не будет заключаться в том, что больше итераций раздувает сумму, до точки переполнения путем добавления большего количества значений - было бы больше, чем больше итераций дал программе лучший шанс попасть в маловероятную случайную величину, которая привела бы к члену INF.

+0

Я использовал эту технику в комментариях части моего кода. Однако он возвращает то же самое. – zhnzhang61

+0

А теперь, я не узнал это сначала: 'Sa = Sa * i/(i + 1) + St/(i + 1);' Вы должны остаться с этим - есть также вопрос числовой стабильности, которая является улучшением по сравнению с окончательным делением. –

+0

Поскольку похоже, что сумма не переполнена естественным образом, я бы начал подозревать, что один из ее терминов - INF - делает ли 'normal_gen' генерирование случайных значений из нормального распределения? Некоторые версии такого генератора будут иногда генерировать очень большие (отрицательные или положительные) значения, а в вашем коде это значение является частью аргумента 'exp', который идет очень быстро, поэтому, возможно, это не вопрос накопления слишком большая сумма во время многих итераций, но вопрос многих итераций делает более вероятным, что значение INF генерируется случайным образом и добавляется к сумме. –

1

Когда вы увеличиваете число итераций, вы увеличиваете значение суммы. В какой-то момент значение переполняет то, что можно содержать в пределах double, таким образом возвращая значение 1.#INF, которое представляет бесконечность как то, что вы рассчитали. Он делает это, потому что рассчитанное значение больше, чем то, что может быть проведено в паре.

Чтобы устранить проблему, вам необходимо изменить переменную, в которой вы держите сумму, с чем-то, что может содержать большее число, чем double. Отправной точкой будет использование long double.

Другой вариант заключается в создании некоторой логики, которую вы используете после цикла for, чтобы иметь дело с меньшими числами. Как это сделать, будет отличаться в зависимости от того, что именно вы используете для вычисления строки.

+0

В некоторых системах «длинный двойной» имеет ту же точность, что и «двойная», чтобы вам ничего не удалось. – 1201ProgramAlarm

+0

Когда я устанавливаю итерацию на 100000, сумма составляет около 800000. Это должно быть число около 8000000, когда я установил итерацию до 1000000. Как вы думаете, есть ли другие возможные причины? – zhnzhang61

+0

в MSVC (который использует OP) 'long double' точно так же, как' double' –

0

Вы переполняете то, что может удерживать двойной. INF - короткий для бесконечности, который является кодом ошибки, который вы получаете при переполнении с плавающей запятой.

Длинный двойной может или не может быть в зависимости от вашего компилятора. В Microsoft C++ я считаю, что длинные двойные и двойные оба являются 64 битами, поэтому там не повезло.

Ознакомьтесь с библиотекой многоточечной поддержки, она имеет более крупные типы, если вам действительно нужно что-то такое большое и не может повторить вашу математику. Я вижу, вы умножаете кучу, а затем делитесь. Можете ли вы умножить некоторые, а затем разделить, а затем умножить некоторые, возможно, на сохранение пространства?

+0

Я тоже пробовал делиться и умножать. Однако это не работает. – zhnzhang61