tagCANDY CGI VBレスキュー(花ちゃん) - VBレスキュー(花ちゃん)の投稿サンプル用掲示板 - Visual Basic 6.0 VB2005 VB2010
VB2005用トップページへVBレスキュー(花ちゃん)のトップページVB6.0用のトップページ
VBレスキュー(花ちゃん)の投稿サンプル用掲示板
     サンプル投稿用掲示板  VB2005 〜 用トップページ  VB6.0 用 トップページ
小数部を含む値の演算誤差についてVB6.0VB.NET(共通) ( No.0 )  [親スレッドへ]
日時: 2010/01/14 18:34
名前: 花ちゃん

***********************************************************************************
* カテゴリー:[基本コード][][]                                                 *
* キーワード:計算誤差,データ型,浮動小数点数型,正確な計算結果,2進数,2進数        *
***********************************************************************************
タイトル :計算の誤差について
記 事 No :13743
投 稿 日 :2009/06/10(Wed) 22:06
元質問者 :シス
-----------------------------------------------------------------------------------
Debug.Print 100.1 - 90.2
という計算をすると「9.9」のはずが「9.89999999999999」という結果になりました。
これはどうしてなのでしょうか?

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

-----------------------------------------------------------------------------------
記事No :13744
投稿日 :2009/06/10(Wed) 22:31
回答者 :花ちゃん  
-----------------------------------------------------------------------------------
タイトルの計算の誤差や演算 誤差等をキーワードに検索すれば見つかりますよ。

データ型と演算誤差についての注意
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@
------------------------------------------------------------
> 型による精度の所為ということでしょうか?

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


-----------------------------------------------------------------------------------
記事No :13748
投稿日 :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 進型を返す場合です。
詳細はヘルプで、/ 演算子の項を見ておいてください。



 [スレッド一覧へ] [親スレッドへ]