11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

java.text.DecimalFormatの四捨五入が失敗するバグ

Last updated at Posted at 2015-01-14

Javaで「小数第N位以下を四捨五入したい」「ある桁はゼロ埋めしたいけど、あとは桁があるときだけ表示したい」などなど、数値を文字列で表現したい場合には、 java.text.DecimalFormat を使うのが便利ですね。

今回はこのDecimalFormatで四捨五入が失敗するバグを見つけたので紹介します。

環境

$ java -version
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
  • OS: OS X Yosemite 10.10.1
  • JDK: Oracle JDK 1.8 u25

現象

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

class Java8DecimalFormat {
    public static void main(String[] args) {
        final DecimalFormat df = new DecimalFormat("0.##");
        df.setRoundingMode(RoundingMode.HALF_UP);

        String hoge = df.format(10.145);
        System.out.println(hoge); // 10.14

        String fuga = df.format(13.555);
        System.out.println(fuga); // 13.55
    }
}

四捨五入を期待して RoundingMode.HALF_UP を指定しているにもかかわらず、挙動としては切り捨てになってしまっています。

実際バグらしい

[#JDK-8062013] DecimalFormat / rounding / HALF_UP - Java Bug System

OpenJDKのバグトラッカーに同様の報告が上がっていました。ver1.8.0u20にも同様のバグがあったので、それなりに前に混入したバグなのかもしれませんね。

テストコードの処理系をJDK1.7からJDK1.8に切り替えた途端にいくつかのテストが通らなくなり、原因を色々探した結果がこれでした。さて、どうしたものか。。。

追記: 1/15 12:40

問題切り分けのために、他のパターンでも実験してみました。

Java7環境

jdk1.7.0_21
String hoge = df.format(10.145);
System.out.println(hoge); // 10.15

String fuga = df.format(13.555);
System.out.println(fuga); // 13.56

JDK1.7.0u21環境での実行結果です。期待した結果が出ています。

Java8環境別バージョン

10.145 は 10.1449(以下略)という内部数値だったはず

コメント欄で上記のご意見をいただいたので、もう一桁加えてみました。

jdk1.8.0_25
String hoge = df.format(10.1451);
System.out.println(hoge); // 10.15

String fuga = df.format(13.5551);
System.out.println(fuga); // 13.56

期待した結果が出ました。

おまけ:Java8環境でバイナリ的に綺麗になるように

丸め誤差の問題であれば、バイナリ的に綺麗になる $2^{-3}=0.125$ を処理する分には問題が起きないはずですね。

jdk1.8.0_25
String piyo = df.format(0.125);
System.out.println(piyo); // 0.13

期待した結果が出ました。

まとめ

というわけで、古き良き浮動小数点の丸め誤差問題でした。

「java.text.DecimalFormatの四捨五入が切り捨てになるバグ」というタイトルで公開しましたが、実際には切り捨てではなく「丸め誤差で5が4扱いになってしまう」という現象でしたので、少しタイトルを変えました。

11
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?