mql5语言的特点、微妙之处以及技巧 - 页 144

 
Igor Makanu:

关于NaN,你需要额外检查

那里有一个正常的数字。检查很简单--除以你所除的东西,然后检查是否为零。

 
fxsaber:

那里有一个正常的数字。检查很简单--你除以什么,你就检查什么为零。

但你在这段代码中没有检查零。

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

如果在8个字节的双字节中没有0x000000000000的值,MQL中的 表达式Num ?将为真

在任何其他情况下,这个表达式(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
 
嗯,为什么在除以零 的时候会有异常被抛出?那么会有inf,有一些废话。
 
Igor Makanu:

你的问题一般是双倍的准确性问题--它一般与特定类型的处理器相联系,这就是为什么上面关于错误不可复制的评论

它在任何类型上都是可重复的。说实话,我不明白为什么有这么多关于这个话题的谈话。好吧,他们把两个非零的数字相乘,得到了零。没有魔法。


每个人都明白这个代码,不是吗?

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.1 * Num ? 是一个bool - 它是一个简单的存在于8个字节的双数中的一个比特。

而当0.1*Num是一个双数时,如果你失去了精度,你可以去找Nan,得到一个零除数

 
我没有得到这个消息。
#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是一个非最小的正双数。

我认为第二个数字有无效的双倍格式,但它在打印机和操作中仍然是有效的。

 
fxsaber:
我没有在这里得到它。


DBL_MIN不是一个最小的正双数。

那么,将DBL_MIN 与NaN进行比较--结果将是bool,对吗?

如果你用1.0除以一个非数字,我们会得到一个异常,正如上面所说的那样

一般来说,事实上,开发人员已经写了很多次,与大于/小于/等于双数的比较是不正确的 - 这是真的,有时你可以得到一个非数字,比较结果将是不可预测的。