- - PR -
Int関数について
投稿者 | 投稿内容 | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2008-04-01 00:04
1回目と2回目で結果が違う、というのはよく分からない仕様ですが、以下の掲示板でちょっと気になる発言があったので、引用します。
http://www2.moug.net/bbs/exvba/20080330000005.htm 以下、引用 たとえば、 Debug.Print VBA.Int(0.3 + 0.7) ' 結果は1 Debug.Print Int(0.3 + 0.7) ' 結果は0 前者は通常のVBAの関数呼び出しですから、 演算結果の値を引数(Variant型)へ格納 する時にDoubleの精度に丸められて、 関数には 1 が渡ることになりますが、 引用ここまで。 Unibonさんのコードで e = Int(a * b) で結果が e = 743 になっていましたが、VBA.Intを使うと e = 744 になりました。(VBA: Retail 6.5.1.24) Currency型などを使う方法がよく対策にありますが、接頭語VBAを使うというのもあるんですね。 | ||||||||||||||||||||||||||||||||
|
投稿日時: 2008-04-30 16:39
VB6とVB.NETで計算結果が異なることについて
不思議に思ったので、下記プログラムをVB.net2003および、 同様のプログラムをVB6で実行してみました。 また、VB6の浮動小数点演算では、少なくとも仮数部を64bitで内部演算している ようです 以下は、VB.net2003で確認しています。VB2005でも動くと思います
最初のforループは、小数点の加算による誤差の累積のチェックと、printしたときの ".ToString()"と."ToString("E17")"の得られる結果の違いをみました。 VB.net2003やVB2005では、 結果を出力する場合(Double型の変数を10進数に変換して表示するとき)、 Doubleの有効桁15.95桁のうち、10進数の16桁目の値を四捨五入して丸めている ことがわかりました。 最初のループの結果です
このように.ToString()でデフォルトの変換を行うと、10進数の16桁目を四捨五入して 丸めているようです。 VB6でも同様なことを実行すると、最初のループのように出力する方法が分かりませんでしたが、 三番目のループで
と、i=20でint(0.15 * i)を直接計算した結果と int(j) (0.15*iを一旦変数"j"に代入後にint()関数を実行した)の結果が、異なる値となりました。 これは iの値が20の倍数で周期的に起きています。(十進数の計算では、答えが整数になるiの値) unibonさんの
と同じ様な結果となりました、 VB6で同様に実行した二番目のループでは(コンパイルしないで実行)
と、i=5やi=2053 で"j-(0.15*i)= 2.77555756156289E-17"の値になっています。 この値は2進数で 1.111111111111111111111111111111111111111111111111101110011101101b*10b^-111000b (マイクロソフトのPowerToyの"Power Calculator"の結果より) (http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx) ≒ 1*2^-55(10進数) という値です。また、i=2053のときの 0.15*i の整数部は308なので、二進数で 9bitと合計64bitの桁数が必要になります。VBの倍精度浮動小数点型は、MSDN Helpより
と、IEEEの倍精度と同等の表現のため、Doubleの仮数部は53bit(52bit+1bit)と思われ、 Doubleだけでは、i=2053 で"j-(0.15*i)=-2.77555756156289E-17"となることはありません。 これは、unibonさんの http://hanatyan.sakura.ne.jp/logbbs1/wforum.cgi?mode=allread&no=3276&page=0 の中にもありましたが、多分 インテルのプロセッサの浮動小数点ユニット(x87 FPU)の 内部精度が、倍精度の64bitより多い 80bit(仮数部64bit)で計算されているためでは ないかと、思います。 VC++とインラインアセンブラでFPU命令を実行した場合、1)の場合の差分 "j-(0.15*i)"の計算結果と、VB6の計算結果が一致しているようです。 なお、FPU命令を直接実行した時のi=20のint(0.15*i)の結果は、
1)と2)で結果が異なるのは、FPU内では4)のように計算精度を指定しない場合は、常に 80bit(仮数部64bit)で計算が行われ、倍制度型の変数に代入するときに「丸め」が、 行われるためと思われます。 1),2)では、i=20での0.15*iの値は、"3"より小さい値ですが、倍精度への丸めにより "3"より大きな値となるためと思われます。(同様なことがVB6でも行われているものと 思われます)
いままで、十進数の"0.15"を倍精度に変換するとき、有効bit数に収まらない場合は 切り下げていると思っていましたが、上述のように、IEEE 754 標準規格に沿った "Unbiased"で丸めているようです。 http://ja.wikipedia.org/wiki/IEEE_754 http://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86 を、参照 その他、下記のURLも、参考になりました。 http://docs.sun.com/source/806-4847/ncg_goldberg.html あと、Decimal型とCurrency型の内部表現の考え方はおもしろいですね、 最初はBCDで表現していると思いましたが、整数型を拡張した型なのですね。 |