Loading

ROUTE

ルートゼロの
アクティビティ

BigDecimal equalsとcompareToの違い|0.0と0.00を比較

2

`new BigDecimal("0.0").equals(new BigDecimal("0.00"))` が`false`になるのは、`BigDecimal.equals()`が数値だけでなく`scale`まで比較するためです。数値として同じかを判定したいなら`compareTo()`を使い、`a.compareTo(b) == 0`で確認します。逆に、`1.0`と`1.00`の表現差そのものに意味がある場合は`equals()`が適しています。

まず結論:数値比較はcompareTo、表現一致はequals

比較方法 0.0 と 0.00 比較するもの 主な用途
equals() false 数値 + scale 表現まで同一か確認する
compareTo() == 0 true 数値 金額・数量など数値として同じか確認する
== 通常false 参照先 同じインスタンスか確認する

OracleのBigDecimal APIでは、`compareTo()`は2.0と2.00のように数値が同じでscaleが異なる値を等しいとみなします。一方、`equals()`は数値とscaleの両方が同じ場合だけ`true`になります。


なぜ0.0と0.00はequalsで一致しないのか

`BigDecimal`は、10進数の値だけでなくscaleも保持します。scaleは小数点以下の桁数に関わる情報で、`0.0`はscale 1、`0.00`はscale 2です。そのため数値としては同じ0でも、`equals()`では別の表現として扱われます。

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("0.0");
BigDecimal b = new BigDecimal("0.00");

System.out.println(a.scale());      // 1
System.out.println(b.scale());      // 2
System.out.println(a.equals(b));    // false

用語解説:scale
BigDecimalが保持する小数点以下の桁数に関する情報です。数値が同じでもscaleが違えばequals()はfalseになります。


金額や数量を数値として比べるならcompareToを使う

「100.0円」と「100.00円」を同額として扱いたいような数値比較では、`compareTo()`が意図に合います。戻り値が0なら数値として等しいため、scaleの違いを気にせず判定できます。

BigDecimal priceA = new BigDecimal("100.0");
BigDecimal priceB = new BigDecimal("100.00");

boolean sameAmount = priceA.compareTo(priceB) == 0;
System.out.println(sameAmount); // true
判定 書き方
等しい a.compareTo(b) == 0
a < b a.compareTo(b) < 0
a > b a.compareTo(b) > 0
a <= b a.compareTo(b) <= 0
a >= b a.compareTo(b) >= 0

実装では`-1`や`1`との完全一致より、0より小さい・大きいという形で書くと比較の意図が読み取りやすくなります。


equalsを使うべき場面もある

`equals()`が間違っているわけではありません。数値だけでなく小数桁の表現まで同じであることが要件なら、`equals()`が適切です。たとえば、外部仕様でscaleを含む表現を区別する場合や、テストで正規化前後の表現まで確認したい場合です。

BigDecimal expected = new BigDecimal("12.00");
BigDecimal actual   = new BigDecimal("12.00");

System.out.println(expected.equals(actual)); // true

要件が「同額か」なのか「同じ表現か」なのかを先に決めると、`compareTo()`と`equals()`を選びやすくなります。

HashMap・HashSetではscale差にも注意する

BigDecimalの`hashCode()`もscaleの影響を受けます。Oracle APIでは、数値が同じでもscaleが異なるBigDecimalは通常同じhash codeにならないと説明されています。そのため、`HashSet`や`HashMap`でキーとして扱うときは、数値比較だけを期待している設計とずれないか確認が必要です。

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;

Set<BigDecimal> values = new HashSet<>();
values.add(new BigDecimal("1.0"));
values.add(new BigDecimal("1.00"));

System.out.println(values.size()); // 2

「数値が同じなら同じキーとして扱いたい」という要件では、保存前にscaleを統一する、またはドメイン側で比較規則を決めるなど、コレクションへ入れる前の設計が重要です。

stripTrailingZerosで正規化する場合の注意点

`stripTrailingZeros()`は末尾の0を取り除き、数値的に等しいBigDecimalを返します。ゼロは`BigDecimal.ZERO`になります。ただし、600.0が`6E+2`相当の表現になるようにscaleが負になる場合もあるため、表示形式まで一定になるとは限りません。

BigDecimal a = new BigDecimal("1.0").stripTrailingZeros();
BigDecimal b = new BigDecimal("1.00").stripTrailingZeros();

System.out.println(a.equals(b)); // true

単に数値として等しいかを知りたいだけなら、正規化してから`equals()`するより`compareTo() == 0`の方が意図を直接表せます。`stripTrailingZeros()`は保存形式や正規化ルールをそろえたい場合に検討します。

実務では「比較したい意味」を先に決める

  • 金額・数量として同じかを判定する → compareTo() == 0
  • 大小関係を判定する → compareTo()を0と比較
  • 数値とscaleの両方を一致させたい → equals()
  • HashMap・HashSetのキーに使う → equals/hashCodeがscaleを見ることを確認
  • 末尾0を除いた表現へ正規化したい → stripTrailingZeros()のscale変化も確認

`0.0`と`0.00`が同じかどうかは、Javaの都合だけではなく、アプリケーションが何を「同じ」と定義するかで決まります。比較方法を選ぶ前に、数値の等価性と表現の等価性を分けて考えるのがポイントです。

よくある質問

BigDecimal.ZERO.equals(new BigDecimal("0.0"))はtrueですか?

falseです。BigDecimal.ZEROのscaleは0で、new BigDecimal("0.0")のscaleは1なので、equals()では一致しません。数値として0かを判定するならvalue.compareTo(BigDecimal.ZERO) == 0を使います。

compareTo()が0ならequals()もtrueになりますか?

必ずではありません。数値が等しくてもscaleが違えばcompareTo()は0、equals()はfalseになります。BigDecimalの自然順序はequalsと整合しない点が公式APIにも明記されています。

毎回stripTrailingZeros()してからequals()すればよいですか?

数値比較が目的ならcompareTo()の方が直接的です。stripTrailingZeros()は表現を変え、値によってはscaleが負になることもあるため、正規化ルールとして採用する場合に限って使う方が意図を明確にできます。

既存コードでBigDecimalの`equals()`を使っている箇所を確認し、それが「数値比較」なのか「scaleを含む表現比較」なのかを一度切り分けてみてください。

カジュアル面談はこちら

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。