浮動小数点数型(double、float)

実数型(または浮動小数点型)は、小数部分を持つ値を表します。MQL5 言語には 2 種類の浮動小数点型があります。コンピュータメモリ内での実数値の表現方法は、IEEE 754 標準によって定義され、プラットフォーム、オペレーティングシステムまたはプログラミング言語とは無関係です。

Type

バイトサイズ

最小の正の値

最大値

C++

float

4

1.175494351e-38

3.402823466e+38

float

double

8

2.2250738585072014e-308

1.7976931348623158e+308

double

double という名称は、その数値の精度が float の 2 倍であるということを示しています。ほとんどの場合 double 型が一番便利です。float の限られた精度は多くの場合に充分ではありません。float 型がまだ使用されているのはメモリ節約のためです(これは実数の大きな配列のために重要です)。

浮動小数点定数は、整数部、点( . )と小数部で構成されています。整数部と小数部は十進数の配列です。

例:

  double a=12.111;
  double b=-956.1007;
  float c =0.0001;
  float d =16;

実数には科学表記があり、多くの場合、科学表記での記録は、従来のものよりもコンパクトです。

例:

  double c1=1.12123515e-25;
  double c2=0.000000000000000000000000112123515; // 小数点の後に 24 のゼロ
 
  Print("1. c1 =",DoubleToString(c1,16));
  // 結果:1c1 = 0.0000000000000000
 
  Print("2. c1 =",DoubleToString(c1,-16));
  // 結果:2c1 = 1.1212351499999999e-025
 
  Print("3. c2 =",DoubleToString(c2,-16));
  // 結果:3c2 = 1.1212351499999999e-025

実数は、一般的に 10 進表記が使用されていてもメモリでは限られた精度を持つ 2 進数システムで格納されていることを忘れてはなりません。これが、10進数では正確に表現されている数字の多くが 2 進数システムでは無限小数としてのみ書くことが可能な理由です。

例えば、2 の冪乗である 0.25 が正確に保存されているのに比べて、0.3 及び 0.7 はコンピュータによって無限小数として表されます。

この点から、2 つの実数の平等さを比較することは不正なので推奨されません。

例:

void OnStart()
 {
//---
  double three=3.0;
  double x,y,z;
  x=1/three;
  y=4/three;
  z=5/three;
  if(x+y==z)
     Print("1/3 + 4/3 == 5/3");
  else
     Print("1/3 + 4/3 != 5/3");
// 結果:1/3 + 4/3 != 5/3
 }

どうしても 2 つの実数の平等さを比較する必要がある場合は、2 つの方法があります。最初の方法は、2 つの数の差を比較の精度を指定する小さな数と比較することです。

例:

bool EqualDoubles(double d1,double d2,double epsilon)
 {
  if(epsilon<0)
     epsilon=-epsilon;
//---
  if(d1-d2>epsilon)
     return false;
  if(d1-d2<-epsilon)
     return false;
//---
  return true;
 }
void OnStart()
 {
  double d_val=0.7;
  float  f_val=0.7;
  if(EqualDoubles(d_val,f_val,0.000000000000001))
     Print(d_val," equals ",f_val);
  else
     Print("Different: d_val = ",DoubleToString(d_val,16),"  f_val = ",DoubleToString(f_val,16));
// 結果:Different: d_val= 0.7000000000000000   f_val= 0.6999999880790710
 }

上記の例では epsilon の値は事前定義された定数 DBL_EPSILON より小さく出来ないことにご注意ください。この定数の値は 2.2204460492503131e-016 です。float 型に対しての定数は FLT_EPSILON = 1.192092896e-07 です。これらの値は  1.0 + DBL_EPSILON = 1.0(float 型の値に対しては 1.0 + FLT_EPSILON!= 1.0)を満たす最小値です。

2 つ目の方法では 2 つの実数の正規化後の差分とゼロを比較します。正規化された数との数学的な操作は、非正規化された結果をもたらすので、正規化された数の差をゼロと比較することは無意味です。

例:

bool CompareDoubles(double number1,double number2)
 {
  if(NormalizeDouble(number1-number2,8)==0)
     return(true);
  else
     return(false);
 }
void OnStart()
 {
  double d_val=0.3;
  float  f_val=0.3;
  if(CompareDoubles(d_val,f_val))
     Print(d_val," equals ",f_val);
  else
     Print("Different: d_val = ",DoubleToString(d_val,16),"  f_val = ",DoubleToString(f_val,16));
// 結果:Different: d_val= 0.3000000000000000   f_val= 0.3000000119209290
 }

数学コプロセッサでの演算は無効な実数をもたらすことがあります。無効な実数との演算の結果は定義されていないため、そのような実数は数学演算や比較の演算に使用することは出来ません。例えば、2 のアークサインを計算しようとすれば、結果は負の無限大です。

例:

  double abnormal = MathArcsin(2.0);
  Print("MathArcsin(2.0) =",abnormal);
// 結果:  MathArcsin(2.0) = -1.#IND

負の無限大の他には正の無限大とNaN(非数)があります。数の無効性を決定するためには MathIsValidNumber() が使用出来ます。IEEE標準によれば、これらには特殊な機械表現があります。例えば、double 型の正の無限大は 0x7FF0 0000 0000 0000 です。

例:

struct str1
 {
  double d;
 };
struct str2
 {
  long l;
 };
 
//--- 開始
  str1 s1;
  str2 s2;
//---
  s1.d=MathArcsin(2.0);       // 無効な数を取得 -1.#IND
  s2=s1;
  printf("1.  %f %I64X",s1.d,s2.l);
//---
  s2.l=0xFFFF000000000000;     // 無効な数 -1.#QNAN
  s1=s2;
  printf("2.  %f %I64X",s1.d,s2.l);
//---
  s2.l=0x7FF7000000000000;     // 最大非数 SNaN
  s1=s2;
  printf("3.   %f %I64X",s1.d,s2.l);
//---
  s2.l=0x7FF8000000000000;     // 最小非数QNaN
  s1=s2;
  printf("4.   %f %I64X",s1.d,s2.l);
//---
  s2.l=0x7FFF000000000000;     // 最大非数 QNaN
  s1=s2;
  printf("5.   %f %I64X",s1.d,s2.l);
//---
  s2.l=0x7FF0000000000000;     // 正の無限大 1.#INF と最小非数 SNaN
  s1=s2;
  printf("6.   %f %I64X",s1.d,s2.l);
//---
  s2.l=0xFFF0000000000000;     // 負の無限大 -1.#INF
  s1=s2;
  printf("7.  %f %I64X",s1.d,s2.l);
//---
  s2.l=0x8000000000000000;     // 負のゼロ -0.0
  s1=s2;
  printf("8.  %f %I64X",s1.d,s2.l);
//---
  s2.l=0x3FE0000000000000;     // 0.5
  s1=s2;
  printf("9.   %f %I64X",s1.d,s2.l);
//---
  s2.l=0x3FF0000000000000;     // 1.0
  s1=s2;
  printf("10.  %f %I64X",s1.d,s2.l);
//---
  s2.l=0x7FEFFFFFFFFFFFFF;     // 最大の正規化数 (MAX_DBL)
  s1=s2;
  printf("11.  %.16e %I64X",s1.d,s2.l);
//---
  s2.l=0x0010000000000000;     // 最小の正の正規化数(MIN_DBL)
  s1=s2;
  printf("12.  %.16e %.16I64X",s1.d,s2.l);
//---
  s1.d=0.7;                   // 0.7 が無限小数だと示す
  s2=s1;
  printf("13.  %.16e %.16I64X",s1.d,s2.l);
/*
1.  -1.#IND00 FFF8000000000000
2.  -1.#QNAN0 FFFF000000000000
3.   1.#SNAN0 7FF7000000000000
4.   1.#QNAN0 7FF8000000000000
5.   1.#QNAN0 7FFF000000000000
6.   1.#INF00 7FF0000000000000
7.  -1.#INF00 FFF0000000000000
8.  -0.000000 8000000000000000
9.   0.500000 3FE0000000000000
10.  1.000000 3FF0000000000000
11.  1.7976931348623157e+308 7FEFFFFFFFFFFFFF
12.  2.2250738585072014e-308 0010000000000000
13.  6.9999999999999996e-001 3FE6666666666666
*/

参照

DoubleToStringNormalizeDouble数値定数