2016-04-22 11 views
0

私は二重の合計を計算する際に質問に出くわしました。繰り返しを100000に設定すると、関数Asian_call_MCはまだ数値を返します。しかし、反復を約500000以上に設定すると、1に戻ります。#INF。誰かがなぜそれが起こり、どうやって解決するのか教えてもらえますか?私はVisual Studio 2013を使用してC++コードを記述しています。#inf C++ビジュアルスタジオ

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の正規分布数を生成します。反復回数を100000に設定すると、合計は約800000になります。 – zhnzhang61

+0

したがって、「大きい」数値を返して境界にはなりません。ある時点では、ドリフト、impl_vol、およびdeltasqrtの値と組み合わされたときにオーバーフロー(約710以上の値)が発生するほど大きな反復回数が発生します。 – 1201ProgramAlarm

答えて

0

平均値を計算するようです。ほとんどの人が平均を計算する方法は、すべての値を合計し、その合計をその合計に寄与する値の数で除算することです。

この方法は、それに関連付けられているいくつかの問題を抱えている - 例えば、一緒に多くの値を追加すると、それを保持する変数に対して大きすぎる合計を与えるかもしれません。

別の技術は、多くの場合、代わりに和の意味で「ランニング」を蓄積する、使用されています。ランニング平均の値は、既に累積されたすべてのサンプルの平均値であるため、オーバーフロー(浮動小数点無限大)値に吹き込まれることはありません(累積サンプルの1つが無限大の場合を除く)。

以下の例は、実行中の平均を計算する方法を示しています。また、合計を計算し、合計/カウントが実行中の平均とどのように比較されているかを示します(同じであることを示すために、合計をオーバーフローさせるのに十分長く実行させません)。

の例では、デモの目的のために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'; 
} 

編集:彼はすでにこれを試してみました - 技術は、私は、壮大な合計を蓄積することにより、平均値を計算するとは異なりOPのコードで実行を

を逃したコメントアウトラインに登場平均的な技術は、ある時点で単にオーバーフローすることはできません。だから、彼はすでにこれを試したことが分かっていて、それが問題を助けなかったことを知り、考えられる原因は反復の用語の一つであるINFそのものである。 1つのINFタームが追加されるとすぐに、累積された合計または平均はINFになり、INFのままになります。

関数の呼び出しの引数の中で、コードの最も可能性が高い部分はnormal_gen()でした。 normal_gen()という名前は、通常分布のランダムな値のソースのように聞こえる。通常の実装ではBox-Muller変換を使用していますが、平均から約7標準偏差を超える値を生成することはできません。したがって、Box-MullerジェネレーターがINFを引き起こしていた場合、報告されているよりも反復回数が少なくなるでしょう。しかし、より進んだジェネレータは、より極端な値を生成することができます。理想的には、正規分布は、の任意の有限実数値を生成する確率はゼロではありません。

ランダムに大きなノーマルサンプルが問題の原因であった場合、反復カウントの増加との相関は、より多くの値を追加することによってオーバーフローのポイントまで合計を膨らませるほどの反復ではありません。プログラムにINFの言葉につながる可能性の低いランダムな値を打つチャンスを与えました。

+0

私はそのコードをコメントアウトした部分でこの手法を使用しました。しかし、それは同じことを返します。 – zhnzhang61

+0

ああ、私は最初にそれを認識しませんでした: 'Sa = Sa * i /(i + 1)+ St /(i + 1);'あなたはそれに留まるべきです - 最終的な分割よりも改善された数値的な安定性のものです。 –

+0

合計が自然にあふれていないように見えるので、その用語の1つがINFであると思われ始めます。つまり、 'normal_gen'は正規分布からランダムな値を生成しますか?そのようなジェネレータのいくつかのバージョンでは、非常に大きな(負または正の)値が生成されることがありますが、コード内では、この値は 'exp'への引数の一部です。多くの反復では大きすぎますが、反復回数が多いため、INF値がランダムに生成されて合計に加算される可能性が高くなります。 –

1

繰り返しの回数を増やすと、合計の値が増えています。ある時点で、値はdouble内に含まれる可能性のある値をオーバーフローし、計算した値として無限大を表す1.#INF値を返します。これは、計算された値がカップルで保持できる値よりも大きいために行われます。

は、問題を解決するには、あなたが doubleより大きい数を保持できるものにして合計値を保持している変数を変更する必要があります。出発点は long doubleです。

別のオプションを使用すると、小さい数字を扱っているので、それにforループの後、あなたが持っているロジックの一部で構築することです。これを行う方法は、正確にあなたが計算する文字列に応じて異なります。

+0

一部のシステムでは、 'long double'は' double'と同じ精度を持ちますので、何も得られない可能性があります。 – 1201ProgramAlarm

+0

反復回数を100000に設定すると、合計は約800000の数値になります。反復回数を1000000に設定すると、約8000000の数値になるはずです。考えられる原因は他にもありますか? MSVCの – zhnzhang61

+0

(OPが使用しています)の 'long double'は' double'とまったく同じです –

0

ダブルが保持できるものがあふれています。 INFは無限大の略で、浮動小数点のオーバーフロー時に発生するエラーコードです。

コンパイルによっては、長い倍精度浮動小数点数が助けになるかもしれません。 Microsoft C++では、long doubleとdoubleの両方が64ビットなので、そこには運がないと思います。

あなたは本当に大きなものが必要とあなたの数学をやり直すことができない場合には、より大きなタイプがあり、ブースト多倍精度のライブラリをチェックしてください。私はあなたが束を掛けて、次に分けているのを見ます。あなたはいくつかを掛け合わせて、次に分けて、スペースを節約するためにいくつかもっと倍増することができますか?

+0

私は除算も乗算も試みました。しかし、どちらもうまくいきません。 – zhnzhang61

関連する問題