アプリ版:「スタンプのみでお礼する」機能のリリースについて

現在、VB6.0を使用しており、小数点の扱いに困っています。

Sub Keisan()
Dim A As String
Dim B As String
Dim C As String

A = 1.29033
B = 1.91458

C = CStr(A + CDec((B - A) / 6) * 3)

MsgBox C
End Sub

上記のプログラムを実行すると、
「1.602455000000001」と表示されますが、
電卓を用いて計算すると、
「1.602454998・・・」となり、微妙に誤差が出てしまいます。

小数点を整数にして計算→元の桁数に戻す、という
処理を行うと、誤差なく求めることが出来ましたが、
「もっとスマートなコードにして」と言われてしまいまして
どうしたものかと思っております。

この誤差を解決する方法は無いでしょうか?

A 回答 (4件)

この問題は、パソコンと電卓との計算の誤差だと考えられます。



1.602455000000001と1.602454998 はとちらも結論から言うと正しいのだが。

問題は、(B-A)/6 が割りきれないところから出ていると思われます。

   0.104041666666・・・・=(B-A)/6

電卓は、1演算の都度、小数点指定桁数で切り捨てて計算するみたいです。
これは、電卓の液晶画面が指定桁数しか表示出来ないところから来ている
と思われます。

今回の問題は、電卓に合わすとの事なので、合わす為には、演算が入る
度に、電卓と同じ桁数で切捨てればOKとの事になります。

ご質問のプログラムもそこを考慮して、CDec((B - A) / 6) とわざとCDec関数を
間に入れていますが、結局小数点9桁で切り捨てられていないので、切捨てられて
いない分だけ計算結果が大きくなります。


下記のプログラムが比較的簡単かなと思います。これでも完璧かどうか
わかりませんが、乗算・除算の計算の度に下のfint関数を呼び出す事です。


ここで注意するのは、fint関数の2番目の引き数です。この引数は、電卓の液晶画面の
表示桁数です。下のサンプルは、表示桁数10桁(小数点も含む)にしています。

Sub Sub Keisan()

  Dim A As Double
  Dim B As Double
  Dim C As String

  A = 1.29033
  B = 1.91458

  C = A + fint((B - A) / 6, 10) * 3

  MsgBox C

End Sub

Function fint(cd as double, ck as integer) As Double
  cm = Format(cd, "#.000000000000000000")
  fint = Left$(cm, ck)
End Function


尚、演算の内容によっては、上のプログラムでもごくまれに電卓と誤差が
出る可能性があります。確立的にはどれだけかは分かりませんが0.01%
位の確立で誤差が発生するかもしれません。(推測です)。

100%絶対に合わすのであれば、ご指摘の通り、整数にて処理を
するのが確実です。ただ、プログラムが複雑になる分バグも出やすく、
簡潔でそこそこ精度があるのであれば、上のプログラムで充分だと
思います。
    • good
    • 0
この回答へのお礼

ありがとうございます!!!
教えて頂いた方法で上手くいきました!!!

電卓の表示桁数で抜き取ればよかったんですね。
言われてみると納得です。
プログラムのこういう瞬間「は」すごく楽しいと思いますw

しかし、小数点の演算箇所は相当数あるんですよね・・・
あれ全部修正してテストするのかと思うと怖くなってきましたw

お礼日時:2008/08/14 00:39

まず、A, B, CがStringの定義なのがアレですね…


誤差を気にするのなら浮動小数点もダメです。
全てVariantで定義して、
A = CDec(1.29033)
とかにしましょう。
あと、計算の中間結果((B-A)/6 とか)も浮動小数点に勝手に変換されると
まずいので、
C = A + (B - A) / CDec(6) * CDec(3)
とかにしたほうがいいかも。
(検証はしてません、あしからず)
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

計算結果は、
A + (B - A) / CDec(6) * CDec(3) = 1.602455
でした(この値でも正しいと思うんですけどねw)。

ちなみにString型で定義してある理由ですが、、、、、不明ですw

お礼日時:2008/08/14 00:54

この演算結果は1.602455が正解なのでは?



CDec((B - A) / 6) * 3 は CDec((B-A)/2) とほぼ等価考えます
0.62425 / 2 = 0.312125
これに 1.29033 を加算するのですから 1.602455 となると思います

CDec((B - A) / 6) * 3部分に関して 有効桁数以下でRound関数で丸めてしまうといった手法も考えられます

C = CStr( A + Round( CDec( ( B - A )/ 6 ) * 3, 8 ) )
といった具合です
    • good
    • 0
この回答へのお礼

回答が遅くなり申しわけありません。

実は「3」はまた別の式を使って求めているので、正確には、
C = CStr(A + CDec((B - A) / 6) * (3))となります。

質問が正確でなくて本当にすいません。
なので、計算の順序を変えるというのは難しいかなと思っています。

回答頂きました式を実行すると、
CStr( A + Round( CDec( ( B - A )/ 6 ) * 3, 8 ) ) = 1.602455
となりました。
正確な式と、桁数を増やしてみましたが、
CStr(A + Round(CDec((B - A) / 6), 12) * (3)) = 1.602455000001
となり、まだ誤差が出てしまいました。

お礼日時:2008/08/14 00:29

除算してから乗算だと誤差が大きくなる可能性は否めないでしょう



逆に 乗算してから除算してやれば期待する結果が得られる場合があります

C = CStr( A + CDec((B-A) * 3 ) / 6)
といった具合で ・・・
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

残念ながらその方法では上手くいかなかったです。。。
(ちなみに「1.602455」と表示されました)

「仕様です」っていって逃げたいです(笑)。

お礼日時:2008/08/08 02:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています