Java | Java 標準ライブラリ:BigDecimal 比較(compareTo)

Java Java
スポンサーリンク

BigDecimal の「比較」がややこしく感じる理由

BigDecimal を使い始めると、ほぼ必ずこう思います。

== じゃダメなのは分かるとして、equalscompareTo って何が違うの?」
「0 との比較を、== 0 みたいに書けないの?」

ここをあいまいにしたまま書き始めると、

equals で比較しているのに、なぜか「0.10」と「0.1」が違う扱いになる
大きい・小さいの判定を書きにくい

といったモヤモヤが溜まっていきます。

BigDecimal の比較は、感覚として

「等しいかどうかを、どのレベルで見るか」
「順序(大小)まで含めて知りたいかどうか」

を意識できるようになると、一気にスッキリします。


equals と compareTo の決定的な違いをまず押さえる

equals は「値+スケール(桁数)」まで厳密に比較する

次のコードを見てください。

import java.math.BigDecimal;

public class EqVsCompare {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.10");

        System.out.println(a.equals(b));      // 1
        System.out.println(a.compareTo(b));   // 2
    }
}
Java

実行すると、多くの場合こうなります。

1 行目(equals の結果)は false
2 行目(compareTo の結果)は 0

なぜかというと、equals

「数値としての値だけでなく、スケール(小数点以下の桁数)」も比較しているからです。

"0.1" は「0.1 (小数第1位)」
"0.10" は「0.10(小数第2位)」

という「表現上の違い」があるので、equals にとっては「別物」です。

一方で compareTo は、「数としての大小」だけ見ます。
0.1 と 0.10 は「数値としては同じ値」なので、compareTo は 0 を返します。

ここが、BigDecimal を使うときにまず理解しておきたい一番大きなポイントです。


compareTo の戻り値の意味をちゃんと覚える

3パターンだけ覚えればいい

compareTo の戻り値は int 型で、意味は3つだけです。

負の値 …「左 < 右」
0 …「左 == 右」
正の値 …「左 > 右」

具体例を見るとイメージしやすくなります。

BigDecimal x = new BigDecimal("10");
BigDecimal y = new BigDecimal("20");

System.out.println(x.compareTo(y));  // -1(x の方が小さい)
System.out.println(y.compareTo(x));  // 1  (y の方が大きい)

BigDecimal a = new BigDecimal("0.10");
BigDecimal b = new BigDecimal("0.1");
System.out.println(a.compareTo(b));  // 0  (数値として等しい)
Java

重要なのは、「戻り値が -1 / 0 / 1 に固定されているとは限らない」ということです。
正確には「負か零か正かだけが意味を持つ」ので、コードでは

compareTo(...) < 0
compareTo(...) == 0
compareTo(...) > 0

のように書くのが正しい使い方です。


BigDecimal と 0 の比較をきれいに書くパターン

よくやりがちな「ダメな書き方」と、比べるべき書き方

ダメなパターンから見てみます。

BigDecimal value = ...;

if (value == BigDecimal.ZERO) {          // 1: これは参照比較(ダメ)
}

if (value.equals(BigDecimal.ZERO)) {     // 2: 桁まで含めて等しいか
}
Java

1 はオブジェクトの参照比較なので論外です。
2 は悪くはありませんが、「0.0」と「0.00」をどう扱いたいかで意味が変わります。

金額などで、「0 円なら桁なんてどうでもいい。0 なら 0 だ」という感覚で比較したいなら、
compareTo を使うのが自然です。

BigDecimal value = ...;

if (value.compareTo(BigDecimal.ZERO) == 0) {
    System.out.println("値は 0 と等しい(スケール関係なし)");
}
Java

同じノリで、「正か負かだけ知りたい」ということもよくあります。

if (value.compareTo(BigDecimal.ZERO) > 0) {
    System.out.println("正の値(0より大きい)");
} else if (value.compareTo(BigDecimal.ZERO) < 0) {
    System.out.println("負の値(0より小さい)");
} else {
    System.out.println("ちょうど 0");
}
Java

「符号を知りたい」という用途では、
value.signum() を使っても同じことができますが、
初心者のうちは compareTo(BigDecimal.ZERO) の方が直感に近く感じるかもしれません。


「境界判定」をすべて compareTo で書けるようにしておく

ある範囲に入っているかのチェック

BigDecimal は直接 >, < 演算子が使えないので、
compareTo を使って大小関係を表現します。

例えば、「0 以上 100 以下か?」をチェックしたいとき。

BigDecimal value = ...;

BigDecimal lower = BigDecimal.ZERO;
BigDecimal upper = new BigDecimal("100");

boolean inRange =
        value.compareTo(lower) >= 0   // 0以上
     && value.compareTo(upper) <= 0;  // 100以下
Java

これが、そのまま

「lower 以上 かつ upper 以下」

の条件に対応しています。

慣れてくると、頭の中で

compareTo(lower) >= 0 → 「lower 以上」
compareTo(upper) <= 0 → 「upper 以下」

と自動翻訳できるようになっていきます。

境界条件を伴うビジネスロジックに強くなる

例えば、ポイント計算や割引率の決定など:

「1000 円未満ならポイント 1%」
「1000 円以上 5000 円未満なら 2%」
「5000 円以上なら 3%」

のようなロジックを BigDecimal で書くとき、
compareTo が自然に出てきます。

BigDecimal amount = ...;

BigDecimal threshold1 = new BigDecimal("1000");
BigDecimal threshold2 = new BigDecimal("5000");

if (amount.compareTo(threshold1) < 0) {
    // 1000 未満
} else if (amount.compareTo(threshold2) < 0) {
    // 1000 以上 5000 未満
} else {
    // 5000 以上
}
Java

double なら if (amount < 1000) と書けますが、
BigDecimal では compareTo を使って、同じことを表現します。


equals を使うべき場面はどこか

「スケールの違いも違いとして扱いたい」ときに使う

普段の「数値としての比較」では compareTo が便利ですが、
equals が意味を持つ場面もゼロではありません。

例えば、「ユーザーに入力してもらった生の値を、そのまま別の値と比較したい」とき。
ルールとして、「0.10 と入力されたら 0.10 として扱うべきで、0.1 とは違う」と決めているようなケースです。

あるいは、「内部表現としてスケールまで含めて一意にしている」ような設計では、
equals で比較しないと意図が崩れます。

とはいえ、実務の金額計算では、

数値として等しいか
閾値との大小判定

の方が圧倒的に多いので、初心者のうちは

「BigDecimal の比較は基本 compareTo。equals は“表現の違いまで気にしたいとき”だけ」

と覚えておくと混乱しにくいです。


まとめ:BigDecimal の compareTo を自分の武器にする

最後に、初心者向けに頭の中を整理しておきます。

BigDecimal の equals は「値+スケール(小数点以下桁数)」まで比べる
BigDecimal の compareTo は「数値としての大小」だけを比べる
compareTo の戻り値は「負(小さい)・0(等しい)・正(大きい)」の3パターン
0 との比較や範囲チェックは、compareTo を使うと自然に書ける

特に、次のような書き方は「定番パターン」として体に覚えさせてしまって構いません。

value.compareTo(BigDecimal.ZERO) == 0 → 0 と等しい
value.compareTo(BigDecimal.ZERO) > 0 → 0 より大きい
value.compareTo(limit) <= 0 → limit 以下

タイトルとURLをコピーしました