- PR -

丸め誤差の対応について、皆様どうしていますか?

投稿者投稿内容
アマ
会議室デビュー日: 2004/08/17
投稿数: 12
お住まい・勤務地: コンクリートジャングル
投稿日時: 2005-06-02 16:13
お世話になります。
皆様、丸めの誤差にはどのように対応されていますか?

初めてdouble型を丸めると誤差が出ることを知って、
下にあるような丸めるクラスを作って対応しようとしています。
(長くてすみません。)

やろうとしていることは、
double型が引数のroundメソッドの中で、
丸める桁数−1の桁数(小数点1位で丸めたいときは2位)で丸めて、
そのあとにもう一度丸めて誤差をなくそうとしています。
これで誤差に対応出来ているでしょうか?

コード:
import java.math.BigDecimal;

public class RoundTest {
	private final BigDecimal BD_ZERO = BigDecimal.valueOf(0);

	/**
	 * 引数val(String型)を引数scaleの小数点の位で丸め
	 * 
	 * @param val - 丸める数値(String)
	 * @param scale - 丸め小数点桁数(int)
	 * @return BigDecimal型
	 */
	public BigDecimal round(final String val, final int scale) {
		BigDecimal ret = null;
		try {
			//引数valをBicDecimal型へ変換する
			BigDecimal bd = new BigDecimal(val);
			//引数scaleの桁数に丸める
			ret = bd.setScale(scale, BigDecimal.ROUND_HALF_UP);
		} catch (RuntimeException e) {
			e.printStackTrace();
			ret = BD_ZERO;	//エラー時は0を
		}

		return ret;
	}


	/**
	 * 引数val(double型)を引数scaleの小数点の位で丸め
	 * 
	 * @param val - 丸める数値(double)
	 * @param scale - 丸め小数点桁数(int)
	 * @return BigDecimal型
	 */
	public BigDecimal round(final double val, final int scale) {
		//丸め誤差対応のため、引数scaleより1つ下の位で丸めた値を取得
		BigDecimal r = round(String.valueOf(val), Math.abs(scale - 1));

		//round(String,int)から結果を求める
		BigDecimal ret = round(r.toString(), scale);

		return ret;
	}

	// 以下、valが他の数値型の物を作成する...予定。
}

一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2005-06-02 16:48
ROUND_HALF_UPというのは四捨五入のことでしょうか。(ソースを良く見ていませんが)
小数点以下を丸めることを考えた場合に、
0.45
は1よりも0に近いですよね。

0.45→0.5 (小数点第2位を四捨五入)
0.5→1 (小数点第1位を四捨五入)

・・・1になってしまいました。遠いのに。
シュン
ぬし
会議室デビュー日: 2004/01/06
投稿数: 328
お住まい・勤務地: 東京都
投稿日時: 2005-06-02 16:58
数値で誤差を出したくないなら、数値はすべてBigDecimalで持ちまわる(必要
なときだけメソッドコールにより明示的に丸める)ことに決め打ちしてしまうのが
一番安全だと思いますが…
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2005-06-02 16:59
引用:

アマさんの書き込み (2005-06-02 16:13) より:
お世話になります。
皆様、丸めの誤差にはどのように対応されていますか?

初めてdouble型を丸めると誤差が出ることを知って、
下にあるような丸めるクラスを作って対応しようとしています。


「丸め誤差」について、誤解がありませんか?

誤差というものは、元々の数値よりも有効桁数の短い数値に
変換するときに生じるものです。doubleに限った話ではありません。

というわけで、誤差をなくすことなど絶対にできません。

できることは、誤差をどのように扱うかをきちんと決めておくことだけです。
引用:

double型が引数のroundメソッドの中で、
丸める桁数−1の桁数(小数点1位で丸めたいときは2位)で丸めて、
そのあとにもう一度丸めて誤差をなくそうとしています。


要するに、doubleをStringにして、期待の桁数より1桁多い状態で四捨五入、
次にそいつを最後の桁で四捨五入してるんですよね。
1回の四捨五入だったら、
0.0-0.4 -> 0
0.5-0.9 -> 1
アマさんのアルゴリズム
0.00-0.44 -> 0
0.45-0.99 -> 1
あんまり解決にはなってないと思います。

まずは、誤差をどのように扱いたいのかをきちんと定義されてはいかがでしょうか。
アマ
会議室デビュー日: 2004/08/17
投稿数: 12
お住まい・勤務地: コンクリートジャングル
投稿日時: 2005-06-02 17:20
お返事ありがとうございます。

僕も勘違い等しておりますが、お付き合いください。
すみません。

では、再度説明させていただきます。
掛け算なんかで、例えば、
158.17 * 50 = 7908.499999999999
のように小数点がなぜか小数点が増えることがありますよね。
これを何とかしたいと思ったのです。
丸めと言っていたのは四捨五入したいということです。

こういうのって発生しませんか?
また、どのように対処されていますか?
やはりシュンさんの仰るとおり全て変数をBigDecimalにするべきなのでしょうか?
そうすると既に書いてしまったPGが...
がるがる
ぬし
会議室デビュー日: 2002/04/12
投稿数: 873
投稿日時: 2005-06-02 18:16
どもです。がると申します。
引用:

アマさんの書き込み (2005-06-02 17:20) より:
掛け算なんかで、例えば、
158.17 * 50 = 7908.499999999999
のように小数点がなぜか小数点が増えることがありますよね。
これを何とかしたいと思ったのです。
丸めと言っていたのは四捨五入したいということです。
こういうのって発生しませんか?
また、どのように対処されていますか?


発生する理由はわかりますよね?
わからない場合…再度質問してください。

対処方法
まず「小数点以下の数字にならないように設計する」のが一番。
どうしてもやむを得ずの場合でも
・整数と整数の四則演算の結果の少数
は認めても
・少数と整数、少数と少数の四則演算
は絶対におきないようにすることが肝要です。

もちろん「きちんと浮動小数点を用いて限りなく精度の高い」計算という
ものも存在しますが。
よほどその辺に習熟していない限り、まずは「少数を回避する」ほうが
よっぽど楽だと思います :-P
びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2005-06-02 18:33
有効桁数を考慮して計算の最後に丸めますかねぇ。

アマさんは「20 / 3」の計算結果をどう表したいですか ?
20.0 / 3.0 だったら ?
20.00 / 3.00 だったら ?
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2005-06-02 18:37
やるのであれば、システム全体で数値の有効桁数を決めておき、
それを守る、という方法でしょうね。

たとえば、
158.17 * 50 = 7908.499999999999
ではなく、
158.17 * 50.00 = 7908.50
となるようにやる。

小数の乗除算では、小数点以下の数字が幾何級数的に増えていきますが、
数値としてはともかく、アルゴリズム上は、最初に決めた範囲外の
桁数は、有効なものではない場合が多いですし。

引用:

やはりシュンさんの仰るとおり全て変数をBigDecimalにするべきなのでしょうか?
そうすると既に書いてしまったPGが...


これは本末転倒では?
すでに記述されたプログラムが、案件を満たしているのなら
そんな心配いらないですよね。

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