大きな数の演算クラスはjavaで提供されており、java.math.BinIntegerクラスとjava.math.BigDecimalクラスがあります。 これら2つのクラスは高精度計算に用いられ、BigIntegerクラスは大きな整数の処理クラス、BigDecimalクラスは大きな数と小数の処理クラスです。 以下にBigDecimalクラスを紹介します。 BigDecimalの実装はBigIntegerを活用していますが、BigDecimalは小数の概念を加えています。 一般的なfloatやダブル型データは、商業計算では数値精度が比較的高いため、科学的計算や工学的計算にのみ使用可能です。そのため、java.math.BigDecimalクラスが使われており、固定点の精度をいかなる精度でもサポートし、通貨の値を正確に計算するために使えます。 以下では、その使用例を挙げて簡単に紹介します
java.math.BigDecimal
今日は教科書を参考にして二進・十進数の変換についてのプログラムを書きましたが、プログラムのアルゴリズム自体は難しくありません。しかし書いてみると、2から10でも10から2でも、21億を超える、つまり整数の範囲を超える数値には良い変換ではないことがわかりました。 0になります。 参考文献では、BigIntegerを使うことでこの問題が解決することがわかっています。 そこでJDKを調べて、何度もテストして、ついに無事に書き上げることができました! 使用体験は以下の通りです:
1. BigIntegerはjava.math.BigIntegerに属しているので、使用前にこのクラスをインポートしてください。 時々、最初にインポートを忘れてしまい、そのプロンプトが定数プロンプトに見つからないこともあります。
2. 多くの構造方法がありますが、現在では時折使われています: BigInteger(String val) BigIntegerの10進文字列表現をBigIntegerに変換します。 BigInteger(String val, int radix) 指定された基数のビッグ整数の文字列表現をビッグ整数に変換します。 int型の2をBigInteger型に変換するには、BigInteger two=新しいBigInteger("2")と書きます。 注2 二重引用符は省略できません
3. BigIntegerクラスはadd()=="+"、divide()=="-"などのint型演算をすべてシミュレートしますが、その内容は数学演算を行う際に直接数学演算に使用できず、内部手法を用いる必要があることに注意してください。 また、そのオペランドもBigInteger型でなければなりません。 例えば、two.add(2) は誤った演算です。なぜなら 2 は BigInteger 型にはならないからです。
4. 計算結果を出力したい場合は、.toStringメソッドを使ってそれを10進文字列に変換してください。詳細は以下の通りです: String toString() このビッグ整数の小数文字列表現を返します。 出力メソッド:System.out.print(two.toString());
5. 使用される3つの関数を説明してください。 BigInteger remainder(BigInteger val) この値は(この% val)のBigIntegerを返します。 BigInteger 否定() BigIntegerは(-this)の値を返します。 int compareTo(BigInteger val) このBigIntegerを指定されたBigIntegerと比較してみます。 残留物は残りを見つけるために使われていました。 否定はオペランドを反対方向に変換します。 比較は以下のように詳細に説明されています: 比較
public int compareTo(BigInteger val)
このBigIntegerを指定されたBigIntegerと比較してみます。 この方法は6つのブール比較演算子(<、==、>、>=、!=、<=)それぞれで好まれます。 これらの比較を行うための推奨文は次の通りです:(x.compareTo(y) <op> 0)、ここで は<op>6つの比較演算子のうちの一つです。
スペックパイア: インターフェース<BigInteger>:比較可能
パラメータ: val - このBigIntegerを比較するBigInteger。 戻る:
タイトル:Javaにおける浮動小数点数の精密計算実装 Ay(オリジナル)改変版 キーワード:Java 浮動小数点数の精密計算
質問: もし次のプログラムをコンパイルして実行したら、何が見えるでしょうか? public class Test{ public static void main(String args[]){ System.out.println(0.05+0.01); System.out.println(1.0-0.42); System.out.println(4.015*100); System.out.println(123.3/100); } };
正しく読んだ! その結果、まさに 0.060000000000000005 0.5800000000000001 401.49999999999994 1.2329999999999999
Javaの単純なfloatやdouble型は操作できません。 この問題はJavaだけでなく、多くの他のプログラミング言語にも見られます。 ほとんどの場合、計算は正確ですが、上記のような誤差を試すために、もう何度か(ループをやることも可能です)を試すことができます。 ようやくなぜBCDコードがあるのか理解できました。 この問題は非常に深刻で、もし9.99999999999999999999999999999元を持っていると、コンピューターは10元分の商品を買えるとは思いません。 一部のプログラミング言語はこの状況に対応するための特殊な通貨タイプを提供していますが、Javaには対応していません。 さて、どうやってこれを直すか見てみましょう。
四捨五入 最初の反応は丸め(rounding)をすることです。 数学クラスの丸法は小数点以下数位を保持する設定はできず、これだけ(2桁を保持)しかできません。 パブリック・ダブルラウンド(ダブルバリュー){ return Math.round(value*100)/100.0; }
残念ながら、上記のコードは動作せず、4.015をこのメソッドに渡すと4.02ではなく4.01が返ってしまいます。これは前述の通りです 4.015*100=401.49999999999994 したがって、正確な丸めを行いたい場合、単純な型で演算を行うことはできません java.text.DecimalFormatもこの問題を解決しません: System.out.println(new java.text.DecimalFormat("0.00").format(4.025)); 出力は4.02です
ビッグデシマル この原理は『Effective Java』という本にも記載されており、floatとdoubleは科学的または工学的な計算にのみ使えます。ビジネスコンピューティングではjava.math.BigDedecimaを使う必要があります。 BigDecimalを構築する方法は4つあり、BigIntegerで作られた2つは気にしません。ですので、さらに2つあります。 ビッグ・デシマル(ダブル・ヴァル) ダブルをビッグデディマルに変換します。 BigDecimmal(文字列 val) BigDecimalの文字列表示をBigDecimalに変換します。
APIは簡単に説明されており、通常は使いやすいです。 私たちは考えずに使うかもしれませんが、何が問題になるのでしょうか? 何か問題が起きたとき、上記の方法のうちどれが十分かという詳細な説明の中に次のような段落があることを知りました。 注:この構成文の結果はやや予測困難な場合があります。 新しいBigDecimal(.1)は正確に0.1と思われがちですが、実際には0.1000000000000000055511151231257827021181583404541015625に等しいです。 これは、.1が正確に二重として表現できない(あるいは有限長の二進分数としても)ためです。 したがって、構成子に渡される長値は、見た目に関係なく正確に0.1には等しくありません。 一方、(String)コンストラクタは完全に予測可能です。新しいBigDecima(「.1」)は予想通り正確に0.1に等しいです。 したがって、一般的にはこの構成要素よりも(String)構造体を使うことが推奨されます。
正確に計算するには、BigDecimalを作るためにStringを使う必要があることがわかりました! 『Effective Java』という本の例では、BigDecimalを作成するためにStringを使っていますが、本書ではこの点が強調されておらず、これは小さな間違いかもしれません。
解決策 この問題を解決したので、原則としてはBigDecimalを使い、Stringも必ず使うことです。 しかし、加算を行いたい場合、まず2つの浮動小数点数をStringに変換し、BigDecimaを作成し、そのうち1つのaddメソッドを呼び出し、もう1つを引数として渡し、さらにBigDecimalの結果を浮動小数点数に変換する必要があります。 そんな面倒な作業に耐えられる? 以下に、操作を簡素化するためにツールクラス「Arith」を提供します。 加算、減算、乗除算、丸め込みなどの静的手法を提供します: パブリック静的ダブルアド(ダブルv1、ダブルv2) パブリック静的ダブルサブ(ダブルV1、ダブルV2) パブリックスタティックダブルマルチ(ダブルv1、ダブルv2) パブリックスタティックダブルディビジョン(ダブルV1、ダブルV2) パブリック静的ダブルディビジョン(ダブルV1、ダブルV2、intスケール) パブリック静的ダブルラウンド(ダブルV、イントスケール)
付録
ソースファイルArith.java:
java.math.BigDecimal; /** * Javaの単純な型では浮動小数点演算を正確に実行できないため、このツールクラスは細かい処理を提供します * 加算、減算、掛け算、除算、丸め込みを含む正確な浮動小数点演算。 */
パブリッククラスArith{
デフォルトの師団運用精度 プライベート静的最終int DEF_DIV_SCALE = 10;
このクラスはインスタンス化できません プライベートArith(){ }
/** * 正確な加算演算を提供します。 * @param v1 が追加されました * @param v2 追加 * @return 2つのパラメータの和 */
パブリックスタティックダブルアッダ(ダブルV1,ダブルV2){ BigDecimal b1 = 新しいBigDecimal(Double.toString(v1)); BigDecimal b2 = 新しいBigDecimmal(Double.toString(v2)); return b1.add(b2).doubleValue(); }
/** * 正確な引き算操作を提供します。 * @param v1 は引きます * @param v2マイナス * @return 2つのパラメータの差 */
パブリックスタティックダブルサブ(ダブルV1、ダブルV2){ BigDecimal b1 = 新しいBigDecimal(Double.toString(v1)); BigDecimal b2 = 新しいBigDecimmal(Double.toString(v2)); b1.subtract(b2).doubleValue(); }
/** * 正確な乗算操作を提供します。 * @param v1 は掛け算されます * @param v2 乗算 * @return 2つのパラメータの積 */
パブリックスタティックダブルマルチ(ダブルV1、ダブルV2){ BigDecimal b1 = 新しいBigDecimal(Double.toString(v1)); BigDecimal b2 = 新しいBigDecimmal(Double.toString(v2)); b1.multiply(b2).doubleValue(); }
/** * 尽きることのない除算が起こる場合、(比較的)正確な除算を行います * 小数点以下の10桁と以下の数字は丸められています。 * @param v1 は分割されています * @param v2 除数 * @return 2つのパラメータの商 */
パブリックスタティック ダブル div(ダブル v1, ダブル v2){ リターン・ディビジョン(v1,v2,DEF_DIV_SCALE); }
/** * (比較的)正確な分割作戦を提供します。 尽きることのない状況が起こる場合、それはスケールパラメータで示されます * 精度を決定し、その後の数字は四捨五入されます。 * @param v1 は分割されています * @param v2 除数 * @paramスケールは、小数点以下数桁まで正確である必要があることを示しています。 * @return 2つのパラメータの商 */
パブリック static double div(ダブル v1、ダブル v2、int スケール){ IF(scale<0){ throw new IllegalArgumentException( 「スケールは正の整数、またはゼロでなければならない」)。 } BigDecimal b1 = 新しいBigDecimal(Double.toString(v1)); BigDecimal b2 = 新しいBigDecimmal(Double.toString(v2)); 返す b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); }
/** * 正確な小数点の丸めを提供します。 * @param v は数の切りくりを必要とします * @paramスケールは小数点以下に予約されています * @return 四捨五入結果 */
パブリック静的ダブルラウンド(ダブルV,intスケール){ IF(scale<0){ throw new IllegalArgumentException( 「スケールは正の整数、またはゼロでなければならない」)。 } BigDecimal b = 新しいBigDecimal(Double.toString(v)); BigDecimal 1 = 新しいBigDecimal("1"); return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } }; |