33

java.math.BigDecimal10進数で無限精度の演算を実行する必要性に対するAnswer™だと思いました。

次のスニペットについて考えてみます。

import java.math.BigDecimal;
//...

final BigDecimal one = BigDecimal.ONE;
final BigDecimal three = BigDecimal.valueOf(3);
final BigDecimal third = one.divide(three);

assert third.multiply(three).equals(one); // this should pass, right?

私は合格することを期待しassertていますが、実際には実行はそこに到達していません:スローされるone.divide(three)原因です!ArithmeticException

Exception in thread "main" java.lang.ArithmeticException:
Non-terminating decimal expansion; no exact representable decimal result.
    at java.math.BigDecimal.divide

この動作はAPIで明示的に文書化されていることがわかります。

の場合divide、正確な商は無限に長い小数展開を持つ可能性があります。たとえば、1を3で割った値です。商に非終了の小数展開があり、正確な結果を返すように演算が指定されている場合、anArithmeticExceptionがスローされます。それ以外の場合は、他の操作の場合と同様に、除算の正確な結果が返されます。

APIをさらに参照すると、実際には、不正確な除算divideを実行するさまざまなオーバーロードがあることがわかります。

final BigDecimal third = one.divide(three, 33, RoundingMode.DOWN);
System.out.println(three.multiply(third));
// prints "0.999999999999999999999999999999999"

もちろん、今の明らかな質問は「ポイントは何ですか?」です。財務計算など、正確なBigDecimal算術が必要な場合の解決策だと思いました。正確に言えない場合、これはどれほど役立つでしょうか?それは実際に一般的な目的を果たしますか、それとも幸いなことにまったく必要のない非常にニッチなアプリケーションでのみ役立ちますか?dividedivide

これが正しい答えではない場合、財務計算の正確な除算に何を使用できますか(つまり、私は財務専攻を持っていませんが、彼らはまだ除算を使用していますよね???)。

4

9 に答える 9

24

これが正しくない場合、財務計算で正確な除算に使用できるものは何ですか? (つまり、私は金融専攻ではありませんが、彼らはまだ除算を使用していますよね???)。

それから私は小学校1年生、1を3で割ると0.33333になると教えられました...つまり、循環小数です。10 進数で表された数値の除算は正確ではありません。実際、固定基数には、その基数で有限精度の浮動小数点数として正確に表すことができない分数 (ある整数を別の整数で割った結果) があります。(番号には繰り返し部分があります...)

除算を伴う財務計算を行う場合、繰り返し発生する端数をどうするかを考慮する必要があります。切り上げ、切り捨て、または最も近い整数などにすることができますが、基本的には問題を忘れることはできません。

BigDecimal javadoc には次のように書かれています。

BigDecimal クラスにより、ユーザーは丸め動作を完全に制御できます。丸めモードが指定されておらず、正確な結果を表すことができない場合は、例外がスローされます。それ以外の場合は、演算に適切な MathContext オブジェクトを提供することで、選択した精度と丸めモードで計算を実行できます。

つまり、丸めについてどうするかを BigDecimal に指示するのはあなたの責任です。

編集-OPからのこれらのフォローアップに応じて。

BigDecimal は、無限に繰り返される小数をどのように検出しますか?

循環小数は明示的に検出されません。特定の演算の結果が、指定された精度を使用して正確に表現できないことを検出するだけです。たとえば、正確に表現するには、小数点以下の桁数が多すぎます。

被除数のサイクルを追跡して検出する必要があります。繰り返し部分がどこにあるかをマークするなど、別の方法でこれを処理することを選択できた可能性があります。

BigDecimal循環小数を正確に表すように指定できたと思います。つまり、BigRationalクラスとして。ただし、これにより実装がより複雑になり、使用コストが高くなります2。また、ほとんどの人は数値が 10 進数で表示されることを期待しており、その時点で小数が繰り返されるという問題が再発します。

要するに、この余分な複雑さとランタイム コストは、 の典型的なユースケースには不適切だということですBigDecimal。これには、会計規則により循環小数の使用が許可されていない財務計算が含まれます。


1 - 優秀な小学校でした ...

2 - 除数と被除数の共通因数を削除しようとする (計算コストが高い) か、それらを際限なく大きくできるようにします (スペースの使用量が多く、後の操作で計算コストがかかります)。

于 2010-05-01T10:50:27.803 に答える
10

クラスは ではありBigDecimalませんBigFractional。あなたのコメントのいくつかから、誰かが可能なすべての数値処理アルゴリズムをこのクラスに組み込んでいないことに不満を言いたいだけのように思えます。金融アプリは無限の小数精度を必要としません。必要な精度 (通常は 0、2、4、または 5 桁) の完全に正確な値。

実際、私は を使用する多くの金融アプリケーションを扱ってきましたdouble。私はそれが好きではありませんが、それはそれらが書かれた方法でした(Javaでもありません)。為替レートと単位換算がある場合、丸めとあざの両方の問題が発生する可能性があります。BigDecimal後者を削除しますが、除算のために前者がまだあります。

于 2010-05-01T17:09:47.513 に答える
6

有理数ではなく小数を扱いたい場合で、最終的な丸め (セントなどへの丸め) の前に正確な計算が必要な場合は、ちょっとしたコツがあります。

最終的な区分が 1 つだけになるように、数式をいつでも操作できます。そうすれば、計算中に精度が失われることはなく、常に正しく丸められた結果が得られます。例えば

a/b + c

等しい

(a + bc) / b.
于 2010-05-01T11:31:14.957 に答える
5

ちなみに、金融ソフトウェアを扱ったことのある人からの洞察は本当にありがたいです。BigDecimalが2倍以上提唱されているのをよく耳にしました

財務レポートでは、スケール= 2およびROUND_HALF_UPのalwasyBigDecimalを使用します。これは、レポートに出力されるすべての値が再現可能な結果につながる必要があるためです。誰かが簡単な計算機を使用してこれをチェックした場合。

スイスでは、ラッペンコインが1枚または2枚なくなったため、0.05に丸められます。

于 2010-05-01T10:19:36.287 に答える
3

財務計算には BigDecimal を使用することをお勧めします。四捨五入は事業者が指定する必要があります。たとえば、金額 (100,00$) を 3 つのアカウントに均等に分割する必要があります。アカウントが余分なセントを取るビジネスルールが必要です。

double、float は、2 の指数ではない 1 の分数を正確に表すことができないため、金融アプリケーションでの使用には適していません。たとえば、0.6 = 6/10 = 1*1/2 + 0*1/4 + 0*1 を検討してください。 /8 + 1*1/16 + ... = 0.1001...b

数学計算では、分母と分子、または式全体を格納するなど、記号的な数値を使用できます (たとえば、この数値は sqrt(5)+3/4 です)。これは Java API の主な使用例ではないため、ここで見つけることができます。

于 2010-05-01T16:24:27.423 に答える
2

保存を分割するには、、を設定する必要がありMATHcontextます

BigDecimal bd = new BigDecimal(12.12, MathContext.DECIMAL32).divide(new BigDecimal(2)).setScale(2, RoundingMode.HALF_UP);

于 2012-06-27T17:25:18.683 に答える
2

の必要性はありますか

a=1/3;
b=a*3;

resulting in

b==1;

金融システムで?そうではないと思います。金融システムでは、計算を行うときにどのラウンドモードとスケールを使用する必要があるかが定義されています。状況によっては、ラウンドモードとスケールが法律で定義されています。すべてのコンポーネントは、このように定義された動作に依存できます。b==1 を返すと、指定された動作を満たさないため、失敗します。これは、価格などを計算する際に非常に重要です。

これは、浮動小数点数を 2 進数で表すための IEEE 754 仕様に似ています。コンポーネントは、情報を失うことなく「より良い」表現を最適化してはなりません。これは契約を破るからです。

于 2010-05-01T13:01:43.283 に答える
1

Java が分数の表現を十分にサポートしていないことは承知していますが、コンピューターで作業する場合、物事を完全に正確に保つことは不可能であることを認識しておく必要があります。少なくともこの場合、例外は精度が失われていることを示しています。

私の知る限り、「10 進数による無限精度演算」は実現しません。小数を扱う必要がある場合は、おそらく問題なく実行できます。例外をキャッチするだけです。それ以外の場合は、Google で簡単に検索すると、Java で分数を操作するための興味深いリソースがいくつか見つかります。

http://commons.apache.org/math/userguide/fraction.html

http://www.merriampark.com/fractions.htm

Javaで分数を表す最良の方法は?

于 2010-05-01T09:37:56.563 に答える
0

コンピューターを使用していることに注意してください...コンピューターには多くの RAM があり、精度には RAM が必要です。したがって、無限の精度が必要な場合は、
(infinite * infinite) ^ (infinite * Integer.MAX_VALUE)テラバイト RAM が必要です...

1 / 30.333333...それを「3で割った1」のようにRAMに保存できるはずであり、それを掛け戻すことができ、1. しかし、Java にそのような機能があるとは思いません...
おそらく、それを行うものを作成するためにノーベル賞を受賞する必要があります。;-)

于 2010-05-01T09:19:12.387 に答える