【検証#01】double型の比較ミス、知らないと致命傷¶
この記事の3行まとめ
- 🔢
double型同士の==比較は信用できない - ✅
NormalizeDoubleまたは差分の絶対値で比較すべし - 💡 FX価格計算で必須の基礎知識
はじめに¶
この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第1弾です。
オリジナル記事の知見に敬意を表しつつ、MQL5での動作確認と追加解説を行います。
時代背景に関する注意
この記事の元となるブログ記事は2009年頃に公開されたものです。
現在のMQL5/MT5では仕様が変更・改善されている可能性があります。
元ネタ
MQL4のdouble型の比較に注意する話
https://fai-fx.hatenadiary.org/entry/20090718/1247924725
(2009年7月18日 公開)
何が問題なのか?¶
プログラミングを学んだ方なら、こんなコードを書いたことがあるかもしれません。
double price = 1.2690 + 0.0003; // 計算で1.2693を得る
double target = 1.2693; // リテラルで指定
if(price == target)
{
Print("一致!"); // ← これが実行されない!?
}
数学的には 1.2690 + 0.0003 = 1.2693 ですよね?
でもコンピュータの世界では、これが一致しないことがあります。
なぜ一致しないのか¶
用語解説:IEEE 754(浮動小数点数)
コンピュータが小数を表現する国際標準規格。
10進数を2進数に変換するため、多くの小数は完全には表現できず、微小な誤差が生じる。
例:0.1 は2進数では無限小数になる。
double型は64ビットの浮動小数点数です。内部的には2進数で表現されるため、10進数の計算結果と微妙にずれることがあります。
実験:内部表現を確認してみよう¶
void OnStart()
{
double price = 1.2690 + 0.0003;
double target = 1.2693;
// 表示上は同じ
Print("price = ", price); // 1.2693
Print("target = ", target); // 1.2693
// 内部的な差を確認
Print("差 = ", DoubleToString(price - target, 20));
// → 0.00000000000000022204... などの微小な値が出る可能性
// 比較結果
if(price == target)
Print("一致");
else
Print("不一致!"); // こちらが実行される可能性あり
}
正しい比較方法¶
方法1:NormalizeDouble を使う¶
用語解説:NormalizeDouble
浮動小数点数を指定した桁数に丸める MQL 関数。
価格の比較では、通貨ペアの小数点桁数(Digits())に合わせて丸める。
方法2:差の絶対値で判定¶
// より汎用的なパターン
double epsilon = _Point; // または任意の許容誤差
if(MathAbs(price - target) < epsilon)
{
Print("一致!");
}
どちらを使うべき?¶
| シーン | 推奨方法 |
|---|---|
| 価格の比較(指値、逆指値) | NormalizeDouble(a - b, _Digits) == 0 |
| インジケータ値の比較 | MathAbs(a - b) < epsilon |
| 厳密なゼロ判定 | NormalizeDouble(a, 8) == 0 |
MQL5での完全動作コード¶
以下はスクリプトとして動作確認できるコードです。
//+------------------------------------------------------------------+
//| DoubleComparisonTest.mq5 |
//| 「とあるMetaTraderの備忘秘録」様の記事を検証 |
//| https://fai-fx.hatenadiary.org/entry/20090718/1247924725 |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link "https://fx-omoshiro-lab.com/"
#property version "1.00"
#property script_show_inputs
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
Print("=== double型比較テスト ===");
// テストケース1: 計算結果 vs リテラル
double calculated = 1.2690 + 0.0003;
double literal = 1.2693;
Print("--- ケース1: 計算結果 vs リテラル ---");
Print("calculated = ", DoubleToString(calculated, 10));
Print("literal = ", DoubleToString(literal, 10));
Print("差 = ", DoubleToString(calculated - literal, 20));
// 危険な比較
if(calculated == literal)
Print("== 比較: 一致");
else
Print("== 比較: 不一致 ← 危険なパターン!");
// 正しい比較1: NormalizeDouble
if(NormalizeDouble(calculated - literal, 5) == 0.0)
Print("NormalizeDouble: 一致 ✓");
else
Print("NormalizeDouble: 不一致");
// 正しい比較2: MathAbs
double epsilon = 0.00001; // 5桁精度
if(MathAbs(calculated - literal) < epsilon)
Print("MathAbs: 一致 ✓");
else
Print("MathAbs: 不一致");
Print("");
// テストケース2: 実際の価格計算(より現実的)
Print("--- ケース2: 実際の価格計算 ---");
double entryPrice = 145.500;
double slDistance = 0.500;
double slPrice = entryPrice - slDistance;
double expectedSL = 145.000;
Print("SL価格 = ", DoubleToString(slPrice, 5));
Print("期待値 = ", DoubleToString(expectedSL, 5));
if(NormalizeDouble(slPrice - expectedSL, 3) == 0.0)
Print("SL判定: 正しく一致 ✓");
else
Print("SL判定: 不一致");
Print("=== テスト完了 ===");
}
実務での応用例¶
指値注文のヒット判定¶
// 悪い例 ❌
if(Bid == targetPrice) // 完全一致を求めている
{
// 執行処理
}
// 良い例 ✓
if(Bid >= targetPrice) // 不等号で判定
{
// 執行処理
}
// より厳密な例 ✓
if(NormalizeDouble(Bid - targetPrice, _Digits) >= 0)
{
// 執行処理
}
トレーリングストップ¶
// 新しいSLが現在のSLより良いか判定
double newSL = Bid - trailDistance * _Point;
double currentSL = PositionGetDouble(POSITION_SL);
// 悪い例 ❌
if(newSL > currentSL) // double同士の不等号は基本的にOKだが...
// より安全な例 ✓
if(NormalizeDouble(newSL - currentSL, _Digits) > 0)
{
// SL更新処理
}
まとめ¶
| ポイント | 内容 |
|---|---|
| 問題 | double型の == 比較は誤差で失敗する |
| 原因 | IEEE 754による2進数表現の限界 |
| 解決策 | NormalizeDouble または MathAbs で比較 |
| 重要度 | 極めて高い(指値・逆指値のバグに直結) |
オリジナル記事への謝辞
この記事は「とあるMetaTraderの備忘秘録」様の貴重な知見をもとに、
MQL5での検証と解説を加えたものです。
オリジナル記事に心より感謝いたします。
ソースコードのダウンロード¶
この記事で紹介したコードをダウンロードできます。
ファイルの種類:スクリプト
保存先: MQL5/Scripts/ フォルダ
使い方: MT5のナビゲーターから「スクリプト」を展開し、チャートにドラッグ&ドロップで実行
01_DoubleComparison.mq5 をダウンロード
関連用語¶
この記事で登場した用語は用語集でも解説しています。