導入: なぜ今さらfloatなのか
Java開発において、数値計算に何を使うかは永遠の課題です。特にfloat型(32ビット浮動小数点数)は、メモリ節約のために安易に選ばれがちですが、精度の低さや計算誤差の問題を理解していないと、致命的なバグを引き起こす原因になります。本記事では、floatの特性と、実務で安全に扱うためのベストプラクティスを解説します。
基礎知識: float型の仕組み
Javaのfloat型は、IEEE 754規格に基づいた32ビットの単精度浮動小数点数です。メモリ使用量はdouble型(64ビット)の半分ですが、その分表現できる範囲と精度が制限されます。
重要なのは、二進数で小数を表現するという点です。0.1のような「十進数で有限の小数」であっても、二進数では無限小数になってしまうため、計算結果にわずかな誤差が生じます。この「丸め誤差」の特性を理解しておくことが、シニアエンジニアとしての第一歩です。
実装/解決策: 正しいリテラルの扱い
Javaでは、小数リテラル(例: 1.23)を記述すると、デフォルトでdouble型とみなされます。そのため、float変数に直接代入しようとするとコンパイルエラーが発生します。末尾に「f」または「F」を付与して、型を明示する必要があります。また、Java 10以降で導入された型推論(var)を使う場合、何も指定しないとdouble型として推論される点にも注意が必要です。
サンプルプログラム: floatの特性を理解するコード
以下のコードは、float型で発生しやすい誤差の例と、型推論時の注意点を示したものです。
public class FloatExample {
public static void main(String[] args) {
// float型への代入:末尾にfを付ける必要がある
float f1 = 1.23f;
// 型推論(var)を使う場合:何も付けないとdoubleになるため注意
var f2 = 1.23f; // 明示的にfを付ければfloatとして推論される
// 誤差の例:0.1fを10回足しても、正確に1.0にはならないことが多い
float sum = 0.0f;
for (int i = 0; i < 10; i++) {
sum += 0.1f;
}
// 結果は 1.0 ではなく、0.99999994 のような値になる可能性がある
System.out.println("計算結果: " + sum);
// 比較の際は直接 == を使わず、許容誤差(イプシロン)を設ける
float epsilon = 0.00001f;
if (Math.abs(sum - 1.0f) < epsilon) {
System.out.println("誤差を考慮すれば等しいとみなせる");
}
}
}
応用・注意点: 現場で役立つアドバイス
1. 金融・決済システムでは絶対に使用しない: 金額計算にはfloatやdoubleを使用してはいけません。必ずBigDecimal型を使用してください。
2. 比較演算子の禁止: float同士の比較に「==」や「!=」を使ってはいけません。計算誤差により、期待通りに動かないことがあります。必ず差分が許容範囲内かを確認するロジックを組みましょう。
3. パフォーマンスの誤解: 近年のCPUではfloatとdoubleの計算速度差はほぼ無視できるレベルです。メモリ容量が極端に制限されている組み込み環境などを除き、迷ったらdoubleを使うのがJavaの標準的な設計指針です。
4. APIの設計: 外部ライブラリや古いAPIでfloatが要求される場合を除き、ビジネスロジック内ではfloatの使用を避ける設計にすることをお勧めします。

コメント