C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: 1 Mod 0.1 を正しく評価するには?


(過去ログ 103 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■61640 / inTopicNo.1)  1 Mod 0.1 を正しく評価するには?
  
□投稿者/ ひろ (12回)-(2011/08/25(Thu) 09:19:36)

分類:[.NET 全般] 

.netフレームワーク 1.1
VS2003
ASP.NET(VB)

1 Mod 0.1 が正しく評価できません。
良い方法はありませんか?


Dim diff As Double = _mMaxValue - _mMinValue
Dim multiplier As Integer = CInt(Math.Log10(diff)) - 1
Dim _mStep As Double = 10 ^ multiplier
If _mMinValue Mod _mStep = 0 Then
    ' そのまま
Else
    ' Min 調整
End If

というようなコードを書いていますが、
0.1で1の剰余を求めると、0にならず0.1になってしまいます。

Double型の精度の問題だと思いますが、
うまく結果を求めるにはどうしたら良いでしょうか?


引用返信 編集キー/
■61641 / inTopicNo.2)  Re[1]: 1 Mod 0.1 を正しく評価するには?
□投稿者/ shu (954回)-(2011/08/25(Thu) 09:31:30)
No61640 (ひろ さん) に返信

双方10倍して計算して10で割るか
Decimalを使用する。
引用返信 編集キー/
■61643 / inTopicNo.3)  Re[1]: 1 Mod 0.1 を正しく評価するには?
□投稿者/ チョリ (4回)-(2011/08/25(Thu) 09:46:22)
以前、ここの掲示板でも議論に挙がっていましたが、
整数以外の数値や負数でModを使うと、予想外の結果になる可能性があります。
(ご認識の通り、Doubleの誤差も影響するでしょう。)

http://bbs.wankuma.com/index.cgi?mode=al2&namber=61153

さて、質問のソースを見ると、

> Dim diff As Double = _mMaxValue - _mMinValue
> Dim multiplier As Integer = CInt(Math.Log10(diff)) - 1
> Dim _mStep As Double = 10 ^ multiplier
> If _mMinValue Mod _mStep = 0 Then
> ' そのまま
> Else
> ' Min 調整
> End If

ですが、具体的にどのような計算を行いたいのでしょうか?
それによっては、他にいい方法があるかも知れません。
例えば、_mMaxValue - _mMinValueの範囲がInteger等の範囲であれば、かなり楽になると思います。


引用返信 編集キー/
■61644 / inTopicNo.4)  Re[1]: 1 Mod 0.1 を正しく評価するには?
□投稿者/ 魔界の仮面弁士 (2327回)-(2011/08/25(Thu) 10:00:59)
2011/08/25(Thu) 10:15:49 編集(投稿者)

# 例示したコードが間違っていたので訂正:

No61640 (ひろ さん) に返信
> 1 Mod 0.1 が正しく評価できません。
たとえば、
 Dim a As Double = 1.0R
 Dim b As Double = 0.1R
 Dim c As Double = a Mod b
は、c が 0.09999999999999995R を返したりしますね。


Double 型は 2進小数として管理される値です。そのため、0.1 という値は
御存知のように誤差を含んでいます。
(0.5 や 0.25 や 0.75 といった値であれば、2進数でも問題無いのですが)

具体的には、十進小数の 0.1 を 二進小数で表した場合、
 0.0001100110011001100110011……
という、無限小数になってしまいます。そのため、Single や Double では
有限桁で打ち切られ、実際には近似値として格納されることになります。

さらに計算段階での誤差も含まれる可能性があり、小数部は期待値と
ならない事があります。


根本的な対策としては、Double を使わないことです。たとえばすべてを
Decimal で演算させた場合には、求める「0.1」という数値を得られます。

 Dim a As Decimal = CDec("1.0")
 Dim b As Decimal = 0.1D
 Dim c As Decimal = a Mod b

もしくは、それぞれの値を 10 倍する事で小数を整数に変換しておき、
最後に結果を 10 で割るという手法もあります。

 Dim a As Integer = 10
 Dim b As Integer = 1
 Dim c As Integer = a Mod b
 Dim result As Decimal = CDec(c) / 10D


Decimal は、10進数で管理される値なので、今回の問題を生じません。
といっても、全く誤差が無いというワケでもないのですけれどね。

1÷3 という値は、10進数では有限桁の小数で表現できない(3進数ならOK)。
1÷10という値は、2進数では有限桁の小数で表現できません(10進数ならOK)。


> Double型の精度の問題だと思いますが、
ということで、個人的には Decimal を使う事をお奨めはしますが、
だからといって、Double 値を Decimal 変換することは避けてください。

これは、近似値から変換した場合には、変換結果が本来期待する値になる
保証が無いため、意味が薄れてしまうためです。

そのため、誤差軽減のために Decimal を使うのであれば、元の入力値や
計算過程において、Single や Double が一切使われる事が無いように
留意しておく必要があるでしょう。
引用返信 編集キー/
■61652 / inTopicNo.5)  Re[2]: 1 Mod 0.1 を正しく評価するには?
□投稿者/ ひろ (13回)-(2011/08/25(Thu) 12:15:12)
2011/08/25(Thu) 12:16:41 編集(投稿者)

shu様、 チョリ様、 魔界の仮面弁士様
回答ありがとうございました。

精度の高いほうで計算されると思っていたので
1D Mod 0.1
をやってみて、結果が変わらなかったので、
Decimalでもだめなんだと思って質問させていただきましたが、

1 Mod 0.1D
としたら、うまくいきました。
確認不足で手間を取らせてしまい、申し訳ございませんでした。


> そのため、誤差軽減のために Decimal を使うのであれば、元の入力値や
> 計算過程において、Single や Double が一切使われる事が無いように
> 留意しておく必要があるでしょう。

VB.netではどちらか片方がDecimal型なら、計算過程、結果ともDecimalに
なるわけではないのですね。

大変勉強になりました。

解決済み
引用返信 編集キー/
■61656 / inTopicNo.6)  Re[3]: 1 Mod 0.1 を正しく評価するには?
□投稿者/ 魔界の仮面弁士 (2329回)-(2011/08/25(Thu) 15:06:58)
No61652 (ひろ さん) に返信
> 1D Mod 0.1
> をやってみて、結果が変わらなかったので、
ついでに、下記の文書にも目を通しておかれる事をおすすめします。

[Visual Basic 言語リファレンス]-[データ型のトラブルシューティング]
http://msdn.microsoft.com/ja-jp/library/ae382yt8%28VS.80%29.aspx
(この文書は、Mod 演算子の項からもリンクされています)

それ以外の演算子についても、どういう型で処理されるのかを
この機会に調べておくと、後々役に立つかと。



> 精度の高いほうで計算されると思っていたので
10進数としての『有効桁数』という意味で言えば、
Decimal は 28〜29 桁分まで確保されていますが、
Double は 15〜16 桁分しかありません。
(整数型では、Long が18〜19桁、ULong が19〜20桁)

しかし表現可能な値の範囲だけで見れば、倍精度浮動小数点の方が広いので、
Double 型として処理させる仕様になったのかも知れません。と勝手な考察。



> VB.netではどちらか片方がDecimal型なら、計算過程、結果ともDecimalに
> なるわけではないのですね。
Mod 演算子の場合、一方が Single または Double なら、結果は浮動小数点型です。
Decimal と整数型、もしくは両方が Decimal の場合のみ、結果も Decimal になります。
http://msdn.microsoft.com/ja-jp/library/se0w9esz.aspx


Mod 演算の前には、それぞれの項が同じ型に揃えられた上で計算されます。
これは通常、「rem」にて剰余演算されることになりますが、Decimal の場合は、
Decimal.Remainder メソッド呼び出しに変換されて処理されます。

http://msdn.microsoft.com/ja-jp/library/system.reflection.emit.opcodes.rem.aspx
http://msdn.microsoft.com/ja-jp/library/system.decimal.remainder.aspx
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -