mql5言語の特徴、微妙なニュアンスとテクニック - ページ 144

 
Igor Makanu:

NaN の場合、追加でチェックする必要があります。

そこには普通の数字がある。チェックは簡単で、割るもので割って、ゼロかどうかをチェックします。

 
fxsaber:

そこには普通の数字がある。チェック方法は簡単で、割ったものがゼロかどうかをチェックするのです。

が、このコードではゼロをチェックしないんですね。

// Неправильно написанная функция.
double WrongFunc( const double Num )
{
  return(Num ? 1 / (0.1 * Num) : 0);
}

MQL の Num ? 式は、8 バイトの double に 0x00000000000000 という値がない場合に真と なります。

それ以外の場合は、この式(Num ?)は真になります。


今月の初めに、MQLでboolをチェックしないことについて議論を持ち出しました。答えは、データの損失はないので必要ありません。同じ問題で、式をNaNに分割してみてください。ダブルバイトが空になりませんか?


今は確認できませんが、おそらくそういうことなのでしょう。

// Неправильно написанная функция.
double WrongFunc( const double Num )
{
  return(Num && !(Num!=Num) ? 1 / (0.1 * Num) : 0);
}
 
Igor Makanu:

でも、このコードではゼロをチェックしないんですね。

今は確認できないが、おそらくこのようにする必要があるのだろう。

うまくいかない。Numは、クラッシュの例では通常の数字です。boolは関係ありません。

 
fxsaber:

うまくいかない。クラッシュの例では、Numは通常の数値です。boolは関係ありません。

NaN検索を確認した

//+------------------------------------------------------------------+
void OnStart()
{
   union ULongTODouble {
      ulong ul;
      double d;
   } ULongDouble;

   ulong i = 0xFFFFFFFFFFFFFFFF;
   while(i > 0 && !IsStopped()) {
      ULongDouble.ul = i;
      if(ULongDouble.d != ULongDouble.d) Print("NaN, i = ", i);
      i--;
   }

}
//_______________________________________________________________________

MQL5では、ループをゼロから開始することができますが、より長く待つ必要があります。

倍精度の問題であり、特定のプロセッサの種類に縛られているため、エラーが再現されないと上記のコメントをしました。


UPDです。

そのように結論づけたほうがよいでしょう。

if(ULongDouble.d != ULongDouble.d) Print("NaN, d = ", ULongDouble.d); // tst1 (EURUSD,H1)	NaN, d = -nan
 
うーん、なぜゼロ ダブりで割ると 例外が全く発生しないんだろう?まあインフもあるだろうし、デタラメもあるだろうけど。
 
Igor Makanu:

ご質問の内容は、一般にダブルの精度に関するものです。一般にダブルは特定の種類のプロセッサーと結びついており、そのため、上のコメントでエラーが再現されないと書かれているのはそのためです

あらゆるタイプで再現可能です。正直なところ、なぜこのテーマでこれほど多くの講演が行われるのか理解できません。まあ、0でない2つの数字を掛け合わせて0にしたわけですから。魔法は使わない。


みんなこのコードを理解してるんだね。

void OnStart()
{
  double d = 0.5;  
  int Count = 1;
  
  while (d *= d)
    Count <<= 1;
    
  Print(Count);
}


ここで、dが0になったときにも、まったく同じような状況がある。

 
fxsaber:

魔法は使わない。

それが、このようにプリントされていないマジックなのです。

void OnStart()
{
   union ULongTODouble {
      ulong ul;
      double d;
   } ULongDouble;

   ulong nan = 18446744073708624091;
   ULongDouble.ul = nan;
   
   Print("ULongDouble.d != ULongDouble.d ", ULongDouble.d != ULongDouble.d);
   double d_value = 0.1 * ULongDouble.d;
   bool b_value = d_value;
   Print("b_value =  ", b_value);
   Print("d_value =  ", d_value);
   Print("1 / d_value =  ", 1 / d_value);

}
//_______________________________________________________________________

2019.10.28 16:25:28.643 tst1 (EURUSD,H4) ULongDouble.d != ULongDouble.d true

2019.10.28 16:25:28.643 tst1 (EURUSD,H4) b_value = true

2019.10.28 16:25:28.643 tst1 (EURUSD,H4) d_value = nan

2019.10.28 16:25:28.667 tst1 (EURUSD,H4) 'tst1.mq5' (28,31) にゼロディバイドがある。



fxsaber

ここで、dが0になったときも状況は全く同じである。

この例では、0.1 * Num ? がboolである場合、8バイトのdoubleの中に1ビットだけ存在することになります。

で、0.1 * Num が double のとき、精度が落ち たら Nan に行って 0 の除算になる

 
あれは出ませんでしたね。
#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

void OnStart()
{
  double Num = 0;
  
  _W(Num) = (uchar)1;
  
  Print(Num < DBL_MIN); // true
  Print(DBL_MIN);       // 2.225073858507201e-308
  Print(Num);           // 4.940656458412465e-324
}


DBL_MIN は最小の正の倍数ではありません。

 
fxsaber:

DBL_MIN は、非最小の正の倍数である。

2番目の数字が無効なダブルフォーマットになっていると思いますが、プリンタ、オペレーションともに有効です。

 
fxsaber:
ここでは手に入りませんでした。


DBL_MIN は最小の正の倍数ではありません。

さて、DBL_MINと NaNを比較すると、結果はboolになりますよね?

また、1.0を非数値で割ると、上で言われたように、例外が発生します。

一般に、開発者が何度も more than / less than / equal to double との比較は正しくないと書いているのは事実です - これは真実で、時には非数値を得ることができ、比較結果は予測できません。

理由: