tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトル計算の誤差について
記事No13743
投稿日: 2009/06/10(Wed) 22:06
投稿者シス
Debug.Print 100.1 - 90.2
という計算をすると「9.9」のはずが「9.89999999999999」という結果になりました。
これはどうしてなのでしょうか?

正確な結果を得るにはどうしたらいいのかわかりません。
調べても上手く見つからなかったので、よかったら教えて下さい
よろしくお願い致します。

[ツリー表示へ]
タイトルRe: 計算の誤差について
記事No13744
投稿日: 2009/06/10(Wed) 22:31
投稿者花ちゃん
> Debug.Print 100.1 - 90.2
> という計算をすると「9.9」のはずが「9.89999999999999」という結果になりました。
> これはどうしてなのでしょうか?

タイトルの計算の誤差や演算 誤差等をキーワードに検索すれば見つかりますよ。

データ型と演算誤差についての注意
http://support.microsoft.com/kb/409744/ja

過去のログより
http://hanatyan.sakura.ne.jp/logbbs1/wforum.cgi?mode=allread&no=3276&page=0

>
> 正確な結果を得るにはどうしたらいいのかわかりません。
> 調べても上手く見つからなかったので、よかったら教えて下さい

Debug.Print 100.1@ - 90.2@

[ツリー表示へ]
タイトルRe^2: 計算の誤差について
記事No13745
投稿日: 2009/06/10(Wed) 22:56
投稿者シス
タイトルで書いておいて「誤差」をキーワードにしていませんでした、、、

通貨型ですか。
早速のお返事ありがとうございます!

にしても理由難しいです。。理解力なくてすみません。
型による精度の所為ということでしょうか?

[ツリー表示へ]
タイトルRe^3: 計算の誤差について
記事No13746
投稿日: 2009/06/11(Thu) 00:10
投稿者花ちゃん
> 型による精度の所為ということでしょうか?

と言うより、今回の場合は、2進数による演算の誤差でしょうか。

例えば、 10/3*3 の計算を電卓でした場合と、PC で計算した場合、答えが違いますよね。

[ツリー表示へ]
タイトルRe^4: 計算の誤差について
記事No13747
投稿日: 2009/06/11(Thu) 03:51
投稿者シス
100.1 か 90.2 かはわかりませんが、2進で表現しきれないということでしょうか?
勉強になります。ご指導ありがとうございます。

[ツリー表示へ]
タイトルRe^5: 計算の誤差について
記事No13748
投稿日: 2009/06/11(Thu) 11:30
投稿者魔界の仮面弁士
> 100.1 か 90.2 かはわかりませんが、2進で表現しきれないということでしょうか?

2 進で誤差なく表せる数は、(2^X) の累積で表せる物です。
 15.75 = (1 * 2^3) + (1 * 2^2) + (1 * 2^1) + (1 * 2^0) + (1 * 2^-1) + (1 * 2^-2) → 1111.11 {2進}
 10.00 = (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0) + (0 * 2^-1) + (0 * 2^-2) → 1010.00 {2進}
  9.00 = (1 * 2^3) + (0 * 2^2) + (0 * 2^1) + (1 * 2^0) + (0 * 2^-1) + (0 * 2^-2) → 1001.00 {2進}
  4.50 = (0 * 2^3) + (1 * 2^2) + (0 * 2^1) + (0 * 2^0) + (1 * 2^-1) + (0 * 2^-2) → 0100.10 {2進}
  1.25 = (0 * 2^3) + (0 * 2^2) + (0 * 2^1) + (1 * 2^0) + (0 * 2^-1) + (1 * 2^-2) → 0001.01 {2進}

10 進で誤差なく表せる数は、(10^X) の累積で表せる物です。
 999.99 = (9 * 10^2) + (9 * 10^1) + (9 * 10^0) + (9 * 10^-1) + (9 * 10^-2)
 100.10 = (1 * 10^2) + (0 * 10^1) + (0 * 10^0) + (1 * 10^-1) + (0 * 10^-2)
  90.20 = (0 * 10^2) + (9 * 10^1) + (0 * 10^0) + (2 * 10^-1) + (0 * 10^-2)
   4.50 = (0 * 10^2) + (0 * 10^1) + (4 * 10^0) + (5 * 10^-1) + (0 * 10^-2)
   1.25 = (0 * 10^2) + (0 * 10^1) + (1 * 10^0) + (2 * 10^-1) + (5 * 10^-2)


ちなみに、「10 ÷ 3 は割り切れない」というのは、10 進や 2 進の場合の話であって、
3 進であれば割り切れる数として表す事が可能です。

「10 ÷ 3」
 10 進数 → 3.3333333… [無限小数]
  3 進数 → 10.1 [ちょうど割り切れる] = (1 * 3^1) + (0 * 3^0) + (1 * 3^-1)
  2 進数 → 11.010101… [循環小数]

「1 ÷ 3」
 10 進数 → 0.333333… [無限小数]
  3 進数 → 0.1 [ちょうど割り切れる] = (1 * 3^-1)
  2 進数 → 0.010101… [循環小数]

「1 ÷ 10」
 10 進数 → 0.1 [ちょうど割り切れる]
  3 進数 → 0.00220022002… [循環小数]
  2 進数 → 0.0001100110011… [循環小数]

「1 ÷ 2」
 10 進数 → 0.5 [ちょうど割り切れる] = (5 * 10^-1)
  3 進数 → 0.111111… [無限小数]
  2 進数 → 0.1 [ちょうど割り切れる] = (1 * 2^-1)
--------------

桁数が無限にあれば、循環小数になっても正しい数値を表せますが、
コンピュータのメモリは有限です。各変数は数バイト程度の領域しかないため、
途中の桁で打ち切られ、その差が『誤差』となります。

100.1 も 90.2 も、10進では小数部は 1 桁で表せていますが、
これらの数を 2進数にすると、いずれも循環小数になってしまうのです。

そして倍精度浮動小数点型では、内部が2進で管理されています。
誤差を含んだ値同士を演算すれば、結果も誤差を含むことになるという事です。



> 通貨型ですか。
> 型による精度の所為ということでしょうか?

通貨型だと問題が出なかったのは、通貨型の内部表現が、8バイトの「整数」であるからです。

たとえば、123.45 という小数があった場合、通貨型の内部では 10,000 倍した「1234500」に相当する
整数値で保持されています。ヘルプの『通貨型 (Currency)』の項も参照しておいてください。

今回の 100.1@ - 90.2@ という演算であれば、「1001000」-「902000」=「99000」として内部処理され、
これが 9.9000 という結果として表されているのだとイメージしてみてください。

なお通貨型の場合、この「10,000 倍」の内部仕様があるために、
「小数部の桁数が 4 桁を超える値を正しく扱えない」という点を覚えておいてください。
---------

また、今回の件とは関係ありませんが、割り算を行う際にも注意してください。VB6 において
 a / b
という除算を行うと、a, b 双方が通貨型であったとしても、結果は Double となります。
殆どの場合、「/ 演算子」は、倍精度浮動小数点数型(Double)となる仕様である事を覚えておいてください。

なお、a, b の型によっては、倍精度浮動小数点数型(Double)型以外を返すパターンもあります。
単精度浮動小数点数型(Single) になる場合と 10 進型を返す場合です。
詳細はヘルプで、/ 演算子の項を見ておいてください。

[ツリー表示へ]
タイトルRe^6: 計算の誤差について
記事No13750
投稿日: 2009/06/12(Fri) 01:41
投稿者シス
>魔界の仮面弁士 様

とても詳しく教えていただいてありがとうございます。
2進で出る誤差について良く分かりました!

通貨型も前もって10,000倍するとか、それゆえの注意点など理解できました!

割り算についても心に留めておいて、開発してみます。
どうもありがとうございました!

[ツリー表示へ]