- PR -

四捨五入

投稿者投稿内容
yamasa
ベテラン
会議室デビュー日: 2003/02/15
投稿数: 80
投稿日時: 2003-07-29 15:47
引用:

unibonさんの書き込み (2003-07-29 14:50) より:
long のキャストは、切捨てではなく、四捨五入に似た丸めかもしれません


C#では double -> long のキャストは切捨て(ゼロに向かって最も近い整数値への丸め)です。
http://www.microsoft.com/japan/msdn/library/ja/csspec/html/vclrfcsharpspec_6_2_1.asp

引用:

0.1を55回加算すると、Console.WriteLine の出力は 5.5 に見えますが、
実際には 5.5 より小さい値(5.499999...)になっているのでしょう。
すなわち、Console.WriteLine が過度に丸めています。
したがって、この段階ですでに変数の値が誤差を持っていることになります。

#以下、あとで追加。
Console.WriteLine が丸めているわけではなく、
数値を文字列に変換するモジュールが丸めています。


この例で私が意図していたのは、
「精度の高いdoubleを使用していても、ごく単純な演算とMath.Round()の組み合わせで
あっという間に誤差が膨れ上がってしまう」
ということです。
ですから、最初の
> Console.WriteLine(value);
でどんな値が出力されるかについてはあまり気にしていません。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-07-29 16:37
unibon です。こんにちわ。

引用:

yamasaさんの書き込み (2003-07-29 15:47) より:
引用:

unibonさんの書き込み (2003-07-29 14:50) より:
long のキャストは、切捨てではなく、四捨五入に似た丸めかもしれません


C#では double -> long のキャストは切捨て(ゼロに向かって最も近い整数値への丸め)です。
http://www.microsoft.com/japan/msdn/library/ja/csspec/html/vclrfcsharpspec_6_2_1.asp


すみません。途中で言語が C# に変わっていたことに気づきませんでした。
(余談ですが VB.NET だとほぼ VB/VBScript と同じようでした。
http://www.microsoft.com/japan/msdn/library/ja/vbls7/html/vblrfVBSpec10_4_1.asp
)

引用:

yamasaさんの書き込み (2003-07-29 15:47) より:
この例で私が意図していたのは、
「精度の高いdoubleを使用していても、ごく単純な演算とMath.Round()の組み合わせで
あっという間に誤差が膨れ上がってしまう」
ということです。


演算の過程で誤差が生じるのではなく、
演算の対象として例示された値(10000000000049999)が、
演算前に、すでに double で表現できる整数の範囲を
超えていることがもっとも大きな要因だと思います。
この値を表現するには約53.15ビット必要ですが、
double の仮数部のビット数は53ビットです。
yamasa
ベテラン
会議室デビュー日: 2003/02/15
投稿数: 80
投稿日時: 2003-07-29 17:28
引用:

unibonさんの書き込み (2003-07-29 16:37) より:
引用:

引用:

0.1を55回加算すると、Console.WriteLine の出力は 5.5 に見えますが、
実際には 5.5 より小さい値(5.499999...)になっているのでしょう。
すなわち、Console.WriteLine が過度に丸めています。
したがって、この段階ですでに変数の値が誤差を持っていることになります。

#以下、あとで追加。
Console.WriteLine が丸めているわけではなく、
数値を文字列に変換するモジュールが丸めています。


この例で私が意図していたのは、
「精度の高いdoubleを使用していても、ごく単純な演算とMath.Round()の組み合わせで
あっという間に誤差が膨れ上がってしまう」
ということです。


演算の過程で誤差が生じるのではなく、
演算の対象として例示された値(10000000000049999)が、
演算前に、すでに double で表現できる整数の範囲を
超えていることがもっとも大きな要因だと思います。
この値を表現するには約53.15ビット必要ですが、
double の仮数部のビット数は53ビットです。


???
ここで私が言及しているのは、
// おまけ: 0.1を55回加算してからRound()
以降の部分についてですよ。
なな
ぬし
会議室デビュー日: 2003/06/22
投稿数: 659
お住まい・勤務地: 愛知県
投稿日時: 2003-07-29 18:42
>> わたしの場合、浮動小数点型をdoubleに統一することで回避しています。
> という発言が「全てdoubleを使っていれば浮動小数点の誤差も気にしなくてよい」という
>誤解を招きかねないと思ってのものです。

たしかに、多数の方々に誤解を招いているようで...失礼しました。

完璧な実装をするならば、(COBOLの処理方法の様に)文字列操作で演算させるしか
無いのではと思っています。

そこまで実装するのは大変で...ということであれば、有効桁数を意識するしかないのでしょうね。
(有効桁数を意識しながら、四捨五入を2回行うとか...←完璧ではありません! 誤解の無いようにお願いします。)
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-07-29 18:55
unibon です。こんにちわ。
#すみません。かなり昔に戻ります。

引用:

yamasaさんの書き込み (2003-07-29 02:08) より:
引用:

Jittaさんの書き込み (2003-07-28 18:32) より:
 10万分の1にして、0.5足して、int型に代入し、10万倍する。FAQ「小数点x桁で四捨五入する」と、考え方は同じです。


そのやり方には大きな落とし穴があります。
計算に浮動小数点を使用していた場合、丸め誤差により
正しくない値になってしまうことがあります。


そのやり方に問題はないはずです。
反例(正しくない値になってしまうこと)はないと思います。

もちろん使う道具(double や float)が持つビット数の制限を
越えない範囲内で使うという前提はあります。
C# (や VB.NET) の仕様では、整数を扱えるビット数の幅が、
float < int < double < long
なので、double を使うよりは long を使ったほうが、
扱える数値の範囲が広くて有利ですが、
それはたまたまそういうビット数に決まっているだけであり、
浮動小数点型の double だから理論的な誤差がある、というわけではないです。
もし long がなかったら double のほうが有利です
(有利・不利はあくまでも扱える数値の範囲だけであり、誤差の有無とは別です)。

#なお、ここでは、あくまでの「そのやり方」(MyRound の実装)について書いています。
#0.1 を 55 回加算するのはまた別です。

引用:

yamasaさんの書き込み (2003-07-29 17:28) より:
???
ここで私が言及しているのは、
// おまけ: 0.1を55回加算してからRound()
以降の部分についてですよ。


すみません。論点を勘違いしていました。
なな
ぬし
会議室デビュー日: 2003/06/22
投稿数: 659
お住まい・勤務地: 愛知県
投稿日時: 2003-07-29 18:57
> 完璧な実装をするならば、(COBOLの処理方法の様に)文字列操作で演算させるしか
> 無いのではと思っています。
> そこまで実装するのは大変で...ということであれば、有効桁数を意識するしかないのでしょうね。
> (有効桁数を意識しながら、四捨五入を2回行うとか...←完璧ではありません! 誤解の無いようにお願いします。)

と書いたものの...さらなる誤解を招くと良くないので...
すみませんが、無視してください。

有識者の方々のご意見を頂きたいと思います。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2003-07-29 19:09
 なんか、いつの間にかふくれあがっていますね。

 「誤差」の問題は、実数を扱うシステムでは常に意識しておかなければならない問題ですね。その点で、yamasaさんのご指摘の『「5万を加えて、10万で割り(int型のまま)、10万倍する」』は、確かにその通りです。できる限り整数で演算するように、頭を持っていかなければなりませんでした。「精度」には気を遣っているのですが、「誤差」は抜けていました。


 ところでdamepgさん、int32では21億4748万3647が最高値です。これ以上の数値にはならないことが保証されているのでしょうか?「万の位」というところから、どれくらいの数値を扱うのか、気になりました。


 なちゃさんの『この辺は意外に(どころか全く)知らない人も結構いてびっくりしたりしますね。』が、大変気になりました。私も入社当時、地図の回転縮小で「誤差」に苦しみました。忘れるとは、苦しみ方が甘かったようです。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-07-29 19:57
unibon です。こんにちわ。

引用:

damepgさんの書き込み (2003-07-28 18:02) より:
Dim aa as Integer = 2367896

一万の位「6」を四捨五入して「2400000」にしたい!


どこかの掲示板で見かけたのですが、
コード:

x = 2367896
y = x - (x Mod 10000)


のようなやりかたもあるそうです。
#「へぇ〜」度が高いですよね。
#こちらの掲示板だったかもしれませんが、良く覚えていません。

#以下、あとで追加。
あれ、これは四捨五入じゃなくて切捨てでした。
四捨五入にも応用できるんでしょうか。

[ メッセージ編集済み 編集者: unibon 編集日時 2003-07-29 20:00 ]

スキルアップ/キャリアアップ(JOB@IT)