概要
最近、小数点の扱いが地域により . か , で異なり予期せぬ動作を起こしたと話題になっていました。
参考: https://twitter.com/sekiyadn/status/1689650550235881472
こちらも初めて知った仕様だったので他言語(java)でも再現するのか試してみました。
確認内容
以下プログラムを実行して結果を比較してみる。
実行時にシステムプロパティで言語と地域を指定しました。
(OS をフランス語に変更すると復旧不可能になりそうなので…)
- 日本
-Duser.language=ja -Duser.country=JP
- フランス
-Duser.language=fr -Duser.country=FR
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
public class DecimalChecker {
public static void main(String[] args) throws ParseException {
System.out.println(Float.parseFloat("3.5"));
try {
System.out.println(Float.parseFloat("3,5"));
} catch (Exception e) {
System.out.println("Float.parseFloat(\"3,5\") " + e);
}
System.out.println(DecimalFormat.getInstance().parse("3.5"));
System.out.println(DecimalFormat.getInstance().parse("3,5"));
System.out.println(NumberFormat.getInstance().parse("3.5"));
System.out.println(NumberFormat.getInstance().parse("3,5"));
System.out.println(new BigDecimal("3.5"));
System.out.println(new BigDecimal("3,5"));
}
}
比較結果
Amazon Corretto 11 と 17 で結果は変わらずでした
XXXFormat の parse で差分が出ています。
| 処理内容 | Ja_JP | fr_FR |
|---|---|---|
| Float.parseFloat("3.5") | 3.5 | 3.5 |
| Float.parseFloat("3,5") | NumberFormatException | NumberFormatException |
| DecimalFormat.getInstance().parse("3.5") | 3.5 | 3 |
| DecimalFormat.getInstance().parse("3,5") | 35 | 3.5 |
| NumberFormat.getInstance().parse("3.5") | 3.5 | 3 |
| NumberFormat.getInstance().parse("3,5") | 35 | 3.5 |
| new BigDecimal("3.5") | 3.5 | 3.5 |
| new BigDecimal("3,5") | NumberFormatException | NumberFormatException |
- Float.parseFloat の例外
java.lang.NumberFormatException: For input string: "3,5"
- new BigDecimal("3,5") の例外
java.lang.NumberFormatException: Character , is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
まとめ
-
例外が発生するならまだ良いが予期せぬ値にパースしている可能性がある
-
ユーザが入力する、設定ファイル等で指定する場合の扱いには気をつける
-
ロケールが指定出来る機能には気をつける
- XXXFormat の getInstance はデフォルトのロケールで取得します。
- ロケールを指定することで実行環境に依らず固定の Format を取得できる
DecimalFormat.getInstance(Locale.FRANCE).parse("3,5")
-
java の場合は
Float.parseFloatやnew BigDeceimalを使うのが無難だと思う