2015年6月10日水曜日

MT4プログラムの小ネタ GetTickCount()とuintの引き算とOS再起動

小ネタです。

MQL4 GetTickCount()の戻り値はuintです。

GetTickCountはミリ秒(※注意)をカウントしています。
49.7日を経過すると、オーバーフローして0に戻る仕様です。
※注意
 GetTickCountで戻される値は、「OS起動からのミリ秒数」です。GetTickCountのカウント値をリセットしたい場合は、OSの再起動が必要です。

オーバーフローしてしまうと差分が正しく取れない?と思われがちですが、ここでuintである意味が生きてきます。
たとえば、前回実行時間を取得する場合、次のようなコードを書きます。
uint beforeTime = GetTickCount();
while( !IsStopped() )
{
//なんか処理
uint currentTime = GetTickCount();
if( currentTime - beforeTime > 60 ) break;
beforeTime = currentTime;
}


たとえば、上記の例で、beforeTimeがオーバーフロー寸前の4294967295として、currentTimeが60だったとします。
単純に考えると60 - 4294967295は-4294967235で、このif文は正しく動作しないように見えます。
ところがuint同士の差分の場合、この結果は61となり、if文が正しく動作します。

単純化するために。4ビット整数値の減算で説明したいと思います。
まず、単純な例として15-1の演算です。15は二進数表現で1111、1は0001です。
1111
-0001
=1110

1110は14となります。

さて、では1-15の場合はどうでしょうか?
0001
-1111
=1110
実は同じように14になります。

0-1の場合、桁借が発生しますが、借りる桁が無い場合、仮想の桁から借りることができると思ってください。

まぁあれこれ書きましたがuint同士の減算の場合
currentTime - beforeTimeもbeforeTime-currentTimeも結果は変わらないということです。そのため、オーバーフローを気にすることなく処理が可能です。

2015.12.28 訂正 すみません。上記は嘘です・・・。順番が違うと結果は異なります。マイナスの値が発生する場合4294967295から引かれた値+1の値となります。

ここで重要なのは「uint」同士の減算ということです。先ほどのプログラムは下記のようにintで受けてもコンパイルは通りますが、オーバーフロー対策は全く行えなくなるので、注意が必要です。
int beforeTime = GetTickCount();
while( !IsStopped() )
{
//なんか処理
int currentTime = GetTickCount();
//正しく動作しない。
if( currentTime - beforeTime > 60 ) break;
beforeTime = currentTime;
}