tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板)
VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板)
[ツリー表示へ]  [ワード検索]  [Home]

タイトル Re: 数字の比較について
投稿日: 2017/01/23(Mon) 14:54
投稿者魔界の仮面弁士
No.11792 への返信ですが、スレッドが深くなってきたので、
元質問の No11781 に繋げます。


> これが合うようになったのが実に感動ものです
> ありがとうございます

元質問は解決したようですが、もう少し補足させてください。


> 自分はエクセルとvbの計算は合わないものと思っていました
> これが合うようになったのが実に感動ものです

Excel の演算精度は、基本的には VB の Double 型と同じです。
https://support.microsoft.com/ja-jp/kb/78113/



> a2 は decimal 型に統一したらどうなるか見ました

いえ、これではまだ統一されていません。

先に回答したとおり、「Decimal ^ Decimal」の時点で Double 精度に落ちていますし、
「Decimal * Double」の演算は Double 型になってしまっていますので、
これはやはり、演算精度は Double のままとなってしまいます。


ひとまず、
 a2 = b2 * c2 * H2 * d2 * g2 * (J2 / (K2 * f2)) ^ 0.5
ではなく、暫定的に
 a2 = b2 * c2 * H2 * d2 * g2 * CDec((J2 / (K2 * f2)) ^ 0.5)
とすることで、演算結果の桁数は増えますが、見た目上精度が上がったように
見えるだけで、途中で Double 型が混じっている時点で、
Double 型を超える精度の結果が得られると言うわけでもありません。

平方根の演算部を、ニュートン法を用いた演算に差し替えるなどして、
Decimal 型を維持できるような処理が必要でしょう。

たとえば Excel VBA や VB6 なら、こんな方法があります。
[getBigSquareRoot]
http://www.vbaexpress.com/kb/getarticle.php?kb_id=887


>  a= 792.99973337178
> a1= 792.999989866276
> a2= 792.999989866276

VB6 でも、a と a1 は同様の結果になるでしょう。
a1 は Single 型、a2 や a3 は Double 型の精度ですね。

ちなみに a2 については、Variant 型を通じて演算することで
VB6 でも Decimal 演算を利用できます。
「 (J / (K * f)) ^ 0.5」が Double になってしまう点は変わりませんが…。


' VB6 の Decimal 演算
Dim a3, b3, c3, d3, f3, g3, H3, J3, K3
b3 = CDec("2018.8")
c3 = CDec("27.03")
d3 = CDec("0.311")
f3 = CDec("553")
H3 = CDec("0.777")
g3 = CDec("1")
J3 = CDec("2")
K3 = CDec("1")
'a3 = b3 * c3 * H3 * d3 * g3 * (J3 / (K3 * f3)) ^ 0.5
'a3 = b3 * c3 * H3 * d3 * g3 * CDec((J3 / (K3 * f3)) ^ 0.5)
'Call MsgBox(a3)



> エクセルの値=792.999989866276

とりあえず小数点以下 1000 桁まで求めてみました。

792.
99998 98662 75502 50219 88355 08659 99282 76083 41929 29331
45504 75192 88153 61645 79045 24809 03166 66313 06469 92862
40882 13367 36051 44300 05657 90842 24989 49252 38830 44803
30998 69961 91578 93238 27365 78310 15993 36082 14845 48677
75605 33965 97881 22750 00588 16356 67810 48442 39092 87815
93328 38654 80597 64153 07679 77261 53570 36281 37877 87682
31249 42717 83267 22791 59147 58105 01020 83200 16008 65155
35561 08568 70965 70057 26518 18795 69492 79896 39775 68793
07083 39204 42861 69416 24960 02805 75975 08171 89668 42391
39426 97149 39058 52156 56371 59953 81178 12076 30474 53677
44469 93453 82167 39304 49148 25715 27853 56867 52969 85833
93051 32840 85677 92793 40213 59407 57197 04683 45350 73337
08726 87805 10781 83023 17996 23860 03241 79108 58340 89929
62079 92284 33126 66774 03448 74046 70640 89447 83173 54567
07318 95161 51415 43476 61799 68639 54255 16090 35670 53632
61432 01943 46129 20599 44610 62012 87214 23860 35875 50456
53739 26704 66056 01936 81565 11765 23438 93339 33140 68792
32592 31586 47336 48987 61708 93958 29674 81588 04422 47022
22649 87403 32807 47686 10898 92530 57663 76117 85191 08672
33904 87068 32648 11566 33904 65108 02733 72607 73937 41756(以下略)


a の結果と比べると、6桁目(整数部3桁+小数部3桁目)までは正しいですが、それ以降の桁に差異があります。
a1 の結果と比べると、14桁目(整数部3桁+小数部11桁目)までは正しいですが、それ以降の桁に差異があります。

これは a が Single 相当、a1 が Double 型相当の演算であるためです。

No11791 の末尾にも書きましたが、
Single 型の有効桁数は 1+23 bit 相当で、
Double 型の有効桁数は 1+52bit 相当です。

そして、 Log10( 2 ^ 23 ) ≒ 9.9236899… なわけです。

2進数で 1+23 桁ということは、10進数に直すと、6.92〜7.22 桁相当の有効桁数。
2進数で 1+52 桁ということは、10進数に直すと、15.65〜15.95 桁相当の有効桁数。
先の a や a1 の精度とほぼ合致していますね。


小数値を Integer 型に格納しようとすれば、実際の値とは ±0.5 程度の誤差が発生しますが、
Double 型も同様です。有効桁数を超える精度の数値は正確に保持できません。

これは Decimal 型であっても同じこと。

仮に、暗黙的型変換による丸めを排除し、X ^ 0.5 を Decimal として
処理できたとしても、保証できる精度は有効桁数までです。


そしてあくまでも「近似値」である以上、最初のコードのように
 If K = 2.3 Then
という判定にしてしまうと、不一致と看做される可能性が生じます。


そこで、誤差を許容できる範囲を定めて、
 If Math.Abs(K - 2.3) < epsilon Then
のように ± の範囲で判定するといった手法がしばしば用いられます。

どの程度の精度を求めるかは、開発するアプリケーションの仕様にもよりますが
上記だとたとえば、epsilon 値は 1E-6 〜 1E-7 ぐらいですかね。

[許容範囲を決めて値を比較する]
http://dobon.net/vb/dotnet/beginner/floatingpointerror.html#section6

[浮動小数点数の等値比較]
http://qiita.com/fujieda/items/90d5465c887f2607e21a

- 関連一覧ツリー をクリックするとツリー全体を一括表示します)

古いスレッドにレスはつけられません。