BigDecimalがfloatまたはdoubleに正確に変換できるかどうかを知る方法は?


10

クラスにBigDecimalは、無損失の変換を保証するいくつかの便利なメソッドがあります。

  • byteValueExact()
  • shortValueExact()
  • intValueExact()
  • longValueExact()

しかし、方法floatValueExact()およびdoubleValueExact()存在しません。

メソッドfloatValue()とのOpenJDKソースコードを読みましたdoubleValue()。両方がそれぞれFloat.parseFloat()とにフォールバックしているように見え、Double.parseDouble()正または負の無限大を返す可能性があります。たとえば、10,000 9の文字列を解析すると、正の無限大が返されます。私が理解しているように、にBigDecimalは無限の内部概念はありません。さらに、100件のように9Sの文字列を解析double与える1.0E100無限大ではなく、精度を失います。

合理的な実装floatValueExact()とは何doubleValueExact()ですか?

私が考えたdouble組み合わせにより、溶液BigDecimal.doubleValue()BigDecial.toString()Double.parseDouble(String)そしてDouble.toString(double)、それは乱雑に見えます。もっと簡単な解決策があるかもしれないので、ここで質問したいと思います。

明確にするために、私は高性能ソリューションは必要ありません。


7
doubleに変換してから、doubleをBigDecimalに変換して戻し、同じ値が得られるかどうかを確認できます。しかし、ユースケースが何であるかはわかりません。倍精度浮動小数点数を使用する場合、厳密でない値を持つことはすでに受け入れています。正確な値が必要な場合は、BigDecimalのままにします。
JB Nizet

回答:


6

docsを読む こと から、それがバリアントで行うことは、小数部の存在をチェックすること、または値が数値型に対して大きすぎて例外をスローすることです。numTypeValueExact

同様のオーバーフローチェックが行われているが、代わりに例外をスローするのではなく、それが返さまたはダブル、のために、またはフロートのために。floatValue()doubleValue()Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITYFloat.POSITIVE_INFINITYFloat.NEGATIVE_INFINITY

したがって、exactfloatとdoubleのメソッドの最も合理的な(そして最も単純な)実装は、変換がを返すPOSITIVE_INFINITYかどうかを単純にチェックする必要がありますNEGATIVE_INFINITY


さらに、その覚えてBigDecimal使用してから来て精度の欠如処理するように設計されたfloatか、doubleそのためのように、大きな無理数のために@JB Nizetは コメントし、あなたが上に追加できる別のチェックは、変換することだろうdoublefloatするために戻ってBigDecimalあなたがまだ得るかどうかを確認するために同じ値。これにより、変換が正しいことが証明されます。

このようなメソッドは次のようになりますfloatValueExact()

public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!Float.isInfinite(result)) {
        if (new BigDecimal(String.valueOf(result)).compareTo(decimal) == 0) {
            return result;
        }
    }
    throw new ArithmeticException(String.format("%s: Cannot be represented as float", decimal));
}

上記のcompareTo代わりにを使用するのはequals、チェックで厳しすぎないようにするためです。equals2つのBigDecimalオブジェクトの値とスケール(小数部の小数部のサイズ)が同じである場合にのみtrueと評価されますが、compareTo重要でない場合はこの違いを見落とします。たとえば、2.0VS 2.00


とても狡猾です。まさに私が必要としたもの!注:Float.isFinite()またはを使用することもできますがFloat.isInfinite()、これはオプションです。:)
kevinarpe

3
BigDecimalのequals()は2.0と2.00を異なる値と見なすため、equalsをequalsよりも優先する必要があります。
JB Nizet

floatとして正確に表現できない値は機能しません。例:123.456f。これは、32ビットfloatと64ビットdoubleの間の仮数(仮数)のサイズが異なるためだと思います。上記のコードでは、次のようにするとより良い結果が得られますif (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {。これは妥当な変更ですか?それとも浮動小数点値の別のコーナーケースが欠けていますか?
kevinarpe

1
@kevinarpe- 123.456f概念的には1.0E100の例と同じです。それは私を打つ、あなたがあれば必要のBigDecimalから変換することを確認するために- >バイナリ浮動小数点が正確に続いている必要があることの問題です。つまり、変換は考慮されるべきではありません。
スティーブンC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.