1. 概要

このチュートリアルでは、Javaでの数値の書式設定に対するさまざまなアプローチと、それらを実装する方法について説明します。

2. String#formatを使用した基本的な数値フォーマット

String#format メソッドは、数値のフォーマットに非常に役立ちます。 このメソッドは2つの引数を取ります。 最初の引数は、表示する小数点以下の桁数のパターンを記述し、2番目の引数は指定された値です。

double value = 4.2352989244d;
assertThat(String.format("%.2f", value)).isEqualTo("4.24");
assertThat(String.format("%.3f", value)).isEqualTo("4.235");

3. 丸めによる10進フォーマット

Javaには、10進数を表す2つのプリミティブタイプfloatおよびdecimalがあります。

double myDouble = 7.8723d;
float myFloat = 7.8723f;

小数点以下の桁数は、実行する操作によって異なる場合があります。 ほとんどの場合、小数点以下の最初の数桁にのみ関心があります。 四捨五入して小数をフォーマットするいくつかの方法を見てみましょう。

3.1. 数値の書式設定にBigDecimalを使用する

BigDecimal クラスは、指定された小数点以下の桁数に丸めるメソッドを提供します。 希望の場所の数に丸められたdoubleを返すヘルパーメソッドを作成しましょう。

public static double withBigDecimal(double value, int places) {
    BigDecimal bigDecimal = new BigDecimal(value);
    bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
}

BigDecimal の新しいインスタンスから始めて、元の10進値を使用します。 次に、スケールを設定することにより、必要な小数点以下の桁数と数値の丸め方法を指定します。 このメソッドを使用すると、double値を簡単にフォーマットできます。

double D = 4.2352989244d;
assertThat(withBigDecimal(D, 2)).isEqualTo(4.24);
assertThat(withBigDecimal(D, 3)).isEqualTo(4.235);

3.2. Math#roundを使用する

Mathクラスの静的メソッドを利用して、double値を指定された小数点以下の桁数に丸めることもできます。 この場合、10 ^ n で乗算し、後で除算することにより、小数点以下の桁数を調整できます。 ヘルパーメソッドを確認してみましょう。

public static double withMathRound(double value, int places) {
    double scale = Math.pow(10, places);
    return Math.round(value * scale) / scale;
}
assertThat(withMathRound(D, 2)).isEqualTo(4.24);
assertThat(withMathRound(D, 3)).isEqualTo(4.235);

ただし、この方法は特定の場合にのみ推奨されます。出力が印刷される前に予想されたとおりに丸められない場合があるためです。

これは、 Math#roundが値を切り捨てているためです。 これがどのように発生するかを見てみましょう。

System.out.println(withMathRound(1000.0d, 17));
// Gives: 92.23372036854776 !!
System.out.println(withMathRound(260.775d, 2));
// Gives: 260.77 instead of expected 260.78

したがって、この方法は学習目的でのみリストされています。

4. さまざまな種類の数値のフォーマット

場合によっては、通貨、大きな整数、パーセンテージなどの特定のタイプの数値をフォーマットしたい場合があります。

4.1. コンマを使用した大きな整数のフォーマット

アプリケーションに大きな整数がある場合は常に、 DecimalFormat を事前定義されたパターンで使用して、コンマで表示したい場合があります。

public static String withLargeIntegers(double value) {
    DecimalFormat df = new DecimalFormat("###,###,###");
    return df.format(value);
}

int value = 123456789;
assertThat(withLargeIntegers(value)).isEqualTo("123,456,789");

4.2. 数字のパディング

場合によっては、指定された長さの数値にゼロを埋めたい場合があります。 ここでは、前述のように、 String#formatメソッドを使用できます。

public static String byPaddingZeros(int value, int paddingLength) {
    return String.format("%0" + paddingLength + "d", value);
}

int value = 1;
assertThat(byPaddingOutZeros(value, 3)).isEqualTo("001");

4.3. 10進数の後に2つのゼロを使用して数値をフォーマットする

小数点の後に2つのゼロがある任意の数値を印刷できるようにするために、事前定義されたパターンを持つDecimalFormatクラスをもう一度使用します。

public static double withTwoDecimalPlaces(double value) {
    DecimalFormat df = new DecimalFormat("#.00");
    return new Double(df.format(value));
}

int value = 12; 
assertThat(withTwoDecimalPlaces(value)).isEqualTo(12.00);

この場合、小数点の後に2つのゼロを指定するパターンを持つ新しいフォーマットを作成しました

4.4. フォーマットとパーセンテージ

時々、パーセンテージを表示する必要があるかもしれません。

この場合、 NumberFormat#getPercentInstanceメソッドを使用できます。 この方法では、 Locale を提供して、指定した国に適した形式で値を印刷できます。

public static String forPercentages(double value, Locale locale) {
    NumberFormat nf = NumberFormat.getPercentInstance(locale);
    return nf.format(value);
}

double value = 25f / 100f;
assertThat(forPercentages(value, new Locale("en", "US"))).isEqualTo("25%");

4.5. 通貨番号のフォーマット

アプリケーションで通貨を保存する一般的な方法は、BigDecimalを使用することです。 それらをユーザーに表示したい場合はどうなりますか? この場合、NumberFormatクラスを使用できます。

public static String currencyWithChosenLocalisation(double value, Locale locale) {
    NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
    return nf.format(value);
}

特定のLocaleの通貨インスタンスを取得し、その値を使用してformatメソッドを呼び出すだけです。 結果は、指定された国の通貨として表示される数値です。

double value = 23_500;
assertThat(currencyWithChosenLocalisation(value, new Locale("en", "US"))).isEqualTo("$23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("zh", "CN"))).isEqualTo("¥23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("pl", "PL"))).isEqualTo("23 500 zł");

5. 高度なフォーマットのユースケース

DecimalFormat は、Javaで10進数をフォーマットする最も一般的な方法の1つです。 前の例と同様に、ヘルパーメソッドを記述します。

public static double withDecimalFormatLocal(double value) {
    DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.getDefault());
    return new Double(df.format(value));
}

私たちのタイプのフォーマットは、特定のローカリゼーションのデフォルト設定を取得します。

10進形式は、国によって数値システムを使用して異なる方法で処理されます。 たとえば、グループ化文字(米国ではコンマ、他のロケールではスペースまたはドット)、グループ化サイズ(米国では3つ、ほとんどのロケールでは3つですが、インドでは異なります)、または10進文字(米国ではドットですが、他のロケールではコンマ)。

double D = 4.2352989244d;
assertThat(withDecimalFormatLocal(D)).isEqualTo(4.235);

この機能を拡張して、特定のパターンを提供することもできます。

public static double withDecimalFormatPattern(double value, int places) {
    DecimalFormat df2 = new DecimalFormat("#,###,###,##0.00");
    DecimalFormat df3 = new DecimalFormat("#,###,###,##0.000");
    if (places == 2)
        return new Double(df2.format(value));
    else if (places == 3)
        return new Double(df3.format(value));
    else
        throw new IllegalArgumentException();
}

assertThat(withDecimalFormatPattern(D, 2)).isEqualTo(4.24); 
assertThat(withDecimalFormatPattern(D, 3)).isEqualTo(4.235);

ここでは、ユーザーがスペースの数に基づいて選択したパターンでDecimalFormatを構成できるようにします。

6. 結論

この記事では、Javaでの数値フォーマットのさまざまな方法について簡単に説明しました。 ご覧のとおり、これを行うための最良の方法はありません。 それぞれに独自の特性があるため、多くのアプローチに従うことができます。

いつものように、これらの例のコードはGitHubから入手できます。