JavaScriptの仕様?について
とあるプログラムを作成中に、行き詰って、検証してみたら、h=0.1のとき、
for(var ii=-127;ii<1271;ii+=0.1*h){
alert(ii);
}
これを実行すれば、
[-127] [-126.99] [-126.98] ...
となると思っていたのですが、Opera及びChromeで動作させると、そうなりません。
... [-127.979999999] ...となります。
なぜなのか教えて下さい。
尚、回避策としてこうしたらうまくいきました。
for(var ii=-127;ii<1271;ii+=h/10){
alert(Math.floor(ii/(h/10))/(10/h));
}
よろしくお願いいたします。
回答(2)
1.

JavaScript に限らず、PC上では、浮動小数点数として扱われる数値には、同じような問題があります。
計算機では、浮動小数点数は、32ビットとか64ビットの2進数で表現する(10進のまま扱う場合もありますが)のですが、
10進数で1/3が無限(循環)小数になるのと同じで、
2進数で1/10は、無限小数になります。
それで、0.1は、限られたビット数の精度では正確に表現できない数ということになるわけです。
そのために、実際には近い(丸められた)数字ということになるのです。
なので、
0.1を10回足しても1.0にならないというようなことが起こります。
ありがとうございます。
えっと、それでは、0.1かけるとおかしくなりますが、10で割ると正しくなるのはなぜでしょうか。
2.

(コメントで書くには長くなってしまったので、回答に書きます。)
一般に誤差のある数値を含めた計算をする場合には、その誤差の影響を考える必要があります。
例えば、
浮動小数点数というのを、数値を正確に表すコトができないで、末尾の数字が少し小さくなる
ということにします。
(0.1は、0.09になり、0.23は、0.229になるというような意味)
(注:2進数小数でも、0.5とか0.75とか0.875とか正確に表せる数値もあります)
すると、
0.1は、0.09となって
0.1×0.1は、
0.09×0.09→0.0081→0.00809(計算結果の丸めが起こる)というようになります。
また、
0.1÷10は、
0.09÷10(整数は精度が落ちない)→0.009→0.0089(計算結果の丸め)
となって、
それぞれ、真の値0.01からの誤差が
0.00191(真の値の19.1%)
0.0011(真の値の11%)
となって、0.1を掛けるより、10で割る方が計算の精度がいいことがわかります。
このように計算の途中には、誤差を大きくしないようにしたり、気を付ける必要があります。
0.1×0.1のような誤差を相乗し累計していくことは、段々誤差を大きくしていきます。
例えば、質問のような場合、ループにおいては、
-12700のように5桁の整数でループをして、
必要なところで100で割るというようなことをするといいかもしれません。
(100で割った計算の結果による丸めだけが誤差になる)
そうか!なるほど!
もっと勉強します。
ありがとうございました。
でも割り切れないのを補正する能力はないんですかね。
コメント(2)
>>2 割り切れないのを補正する能力
JavaScript でいうと、(ちょっと違うかも?)
例えば、Math.roundのような丸めをする関数などがあります。
丸めを指定した桁数で積極的に行うことで、補正出来る場合があります。
一般には、浮動小数点数の計算には、CPUやCPUを補佐するプロセッサ等が、64ビットの計算をハードウェア的に計算できるので、そうした表現できない数というようなデメリットがあるにしても、そのようなことを踏まえた上で、手軽に使うのが普通ですが、
数を浮動小数点数ではなく、2個組みの整数で、有理数を表現したりということで、メモリの許す限りの精度を持たせることもソフトウェア的にできます。
(そうでなければ何万桁もの円周率を求めたりとかできませんね)
それには、当然計算上のコストが掛かってしまいますので、
そうしたトレードオフを踏まえて、計算に必要な精度と、計算方法を考慮する必要があります。




