しんさんの出張所 はてな編 このページをアンテナに追加 RSSフィード

カレンダー
2007 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 08 | 09 |

2009-07-19

[][]第14夜 Java SE 6 の 数値丸めの新機能

たまにはプログラミングの話題でも。パラメータいじるのが好きな人にとってDQ9はシリーズ最高傑作すぎる。


今回はDecimalFormatのJDK 6での新機能について。地味ながらも便利な丸めのお話。派手じゃないわりと誰もが知っている機能のほうが受けがいいらしいので。数値のフォーマットはよく使われる機能だと思いますしね。



通常数値を表示するのにDecimalFormatを使うことは多いです。カンマ区切りで表示などもよく使われる機能です。では丸めはどうするのでしょうか。小数点1桁だけ表示とかよくやりますよね。単純にやると以下のようなコードになります。

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class Format {

    public static void main(String[] args) {
        DecimalFormat df = new DecimalFormat(",##0.0");

        System.out.println( df.format(new BigDecimal("1234.55")) );
    }

}

結果は以下のようになります。

1,234.6

一見四捨五入されているように見えますが、実は違います。以下のコードを実行すると

        System.out.println( df.format(new BigDecimal("1234.45")) );
        System.out.println( df.format(new BigDecimal("1234.55")) );
        System.out.println( df.format(new BigDecimal("1234.65")) );

結果は以下のようになります。

1,234.4
1,234.6
1,234.6

デフォルトでの端数の処理はHALF_EVENモードで行われます。これは最も誤差を減らす丸めモードなのですが、計算中にこれを採用することはあっても表示する場合に使うことはまずないと思います。そもそも切り上げで表示したい場合、切捨てで表示したい場合、四捨五入で表示したい場合と同一の画面や帳票ですら丸めの計算方法が違う場合は珍しくなく、パラメータとして渡す側がそれを意識するのはつらい。事実上ファサードとなるメソッドが必要になります。



J2SE 5.0までは丸めモードがDecimalFormatになかったのでBigDecimal引数側であらかじめ計算しておく必要がありました。その前の1.4では丸め処理用命令が用意されていませんでした。5.0で用意はされましたがぱっと見でわかるようには思えません。そもそも表示する項目ごとにわざわざ計算を記述しないといけないのはつらいものがありました。

たとえばJ2SE 5.0での小数点2桁目を四捨五入する場合以下のようになります。斜め読みで理解するのは難しいですよね。でもみんなこんなコードを日常的に書いていますよね。

        BigDecimal num = new BigDecimal("1234.55");
        MathContext mathContext = new MathContext(num.precision() - num.scale() + 1, RoundingMode.HALF_UP);
        System.out.println(num.round(mathContext) );

数値の表示なんてのはよくある機能です。そのよくある機能がこれだけ煩雑なのはちょっとつらいものがありました。



ではJavaSE 6でDecimalFormatはどう変わったのでしょうか。たとえば最初のコードで四捨五入表示が可能です。

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class Format {

    public static void main(String[] args) {
        DecimalFormat df = new DecimalFormat(",##0.0");
        df.setRoundingMode(RoundingMode.HALF_UP);//★ここが追加されただけ!

        System.out.println( df.format(new BigDecimal("1234.45")) );
        System.out.println( df.format(new BigDecimal("1234.55")) );
        System.out.println( df.format(new BigDecimal("1234.65")) );
    }

}

setRoundingModeメソッドが追加された部分です。実行すると以下のようになります。

1,234.5
1,234.6
1,234.7

非常に簡単ですね。パラメータとして渡す数値をあらかじめ丸めの計算をしなくてよいのでらくちんです。

ほかに切り捨てにしたい場合は

        df.setRoundingMode(RoundingMode.DOWN);|

逆に切り上げにしたい場合は

        df.setRoundingMode(RoundingMode.UP);|

とするだけでいいです。表示の際にはこの3種類があれば十分でしょう。お客さんが指定してくる書式もこの3種であることがほとんどなはずです。


この機能は正確にはNumberFormatで持っていますが、まるめを意識するような桁を指定された書式で印字など細かく扱うときにはDecimalFormatを使わざるを得ないでしょうからDecimalFormatと表記しました。


ちなみに上で軽く説明しましたがBigDecimalは1.4から5.0で大幅に機能が増えていて使い勝手が別物になってます。1.4時代のBigDecimalは頻繁に使う機能すらなく、まじできつい。便利な定数も5.0から追加されました。


これでもまだJ2SE 5.0なんですか?1.4はさらに地獄ですよね?みんな地獄が好きなんですか?

garnetcatgarnetcat 2011/07/13 16:21 J2SE 6 は便利ですよね。
数値フォーマットはおくとして、J2SE 5.0 小数点2桁目を四捨五入する書き方としてこんな書き方はいかがでしょう(間違っていたらすいません)。

( new BigDecimal( "1234.55" ) ).setScale( 1, BigDecimal.ROUND_HALF_UP )

shinshin 2011/07/13 21:01 setScale(int ,int)は5.0以降推奨されない使い方なのでRoundingModeを使いましょう。

このエントリのポイントは値はそのままで、Viewで四捨五入するというところです。Modelの値をそのまま渡していいのです。

MathContext は冗長ですが、指定した桁で四捨五入するという情報を保持しておくということはよく使われるのでわざとroundメソッドで書きました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/shin/20090719/p2
リンク元