0.3 ÷ 0.1 = 2 余り 0.1 ??

ポロリ 2010/03/28 16:19
はじめまして。いつも参考にさせていただいております。

EA作成中に困ったことが発生してしまったので
質問させてください。

割り算の余りを出力するMathModで、
MathMod(1.1,0.1)とMathMod(0.3,0.1)なのですが、
前者は戻り値0、後者は戻り値が0.1となります。

私は両者とも戻り値0にしたいのですが、
どのようにすればよいのでしょうか?

昨日コメントを頂いて既に回答した話ですが、大事なことなので記事にしておきます。


MathMod を使用して、0.3 ÷ 0.1 の余りを求めると…

Print( MathMod(0.3,0.1) );
// 出力値は 0.1   (°▽°;) 

余りが 0.1 になってしまいます..。
何故こんな現象が起きるのかというと、コンピュータの二進数の世界では 0.1 を正確に表現できないのが原因です。

[10進数] 0.1 = [2進数] 0.000110011001100110011…………

1/3 を小数で表現すると 0.3333…と無限に続くのと同様に、2進数の 0.1 は無限に続きます。無限に表記することはできないので、循環小数は途中で打ち切ることになり、そこで誤差(丸め誤差)が生じます。
(これは 0.1 以外にもいろんな数値で起こりえる話です。)


丸め誤差を MQL4 の枠組みで体感するのは難しくて、

#include 
Print( DoubleToStrMorePrecision(0.1*3,16) );
// 出力値は 0.3000000000000001

上記のように 0.1 を3倍(or 6倍,7倍)して、DoubleToStrMorePrecision 関数で、16桁の精度で表示させると、16桁目に誤差らしき数値が現われます。
ちなみに、MQL5 では MathMod(0.3,0.1) は 0.09999999999999998 と出力されるので、丸め誤差の問題に気付きやすいはずです。




…で、今回の問題を回避する方法ですが、MathModに渡る引数が整数となるようにするのが簡便です。

Print( MathMod(0.3*10,0.1*10)/10 );

ただ、これだけでは回避しきれない場合もあるので、

Print(  MathMod(NormalizeDouble(0.3*10,0),NormalizeDouble(0.1*10,0))/10);

とするのが良いかも知れません。。