ダブルを使って商品の量を定義したい人は誰でも、みんなを詰めて行きます

最初に現象を見てください。floatと
doubleの2種類の浮動小数点データの処理に関しては、常に奇妙な現象が発生することがあります。注意を払ったかどうかはわかりません。いくつかの一般的な例を挙げてください。

典型的な現象(1):条件付き判断が期待を超える

System.out.println(1f == 0.9999999f); //印刷:false
System.out.println(1f == 0.99999999f); //印刷:true Nani?
典型的な現象(2):データ変換が予想を超える

float f = 1.1f;
double d =(double)f;
System.out.println(f); //印刷:1.1
System.out.println(d); //印刷:1.100000023841858 Nani?
典型的な現象(3):基本的な計算が期待を超える

System.out.println(0.2 + 0.7);  
 
//印刷:0.8999999999999999 Nani?
典型的な現象(4):自己増加するデータが期待を超える

float f1 = 8455263f;
for(int i = 0; i <10; i ++){     System.out.println(f1);     f1 ++; } // print:8455263.0 // print:8455264.0 // print:8455265.0 // print: 8455266.0 //印刷:8455267.0 //印刷:8455268.0 //印刷:8455269.0 //印刷:8455270.0 //印刷:8455271.0 //印刷:8455272.0 float f2 = 84552631f; for(int i = 0; i <10; i ++){     System.out.println(f2);     f2 ++; } //印刷:8.4552632E7 Nani?+1しませんでしたか?//印刷:8.4552632E7ナニ?+1しませんでしたか?//印刷:8.4552632E7ナニ?+1しませんでしたか?//印刷:8.4552632E7ナニ?+1しませんでしたか?//印刷:8.4552632E7ナニ?+1しませんでしたか?













 










//印刷:8.4552632E7ナニ?+1しませんでしたか?
//印刷:8.4552632E7ナニ?+1しませんでしたか?
//印刷:8.4552632E7ナニ?+1しませんでしたか?
//印刷:8.4552632E7ナニ?+1しませんでしたか?
//印刷:8.4552632E7ナニ?+1しませんでしたか?
ほら、これらの単純なシナリオでの使用法は私たちのニーズを満たすのが難しいので、浮動小数点数(doubleとfloatを含む)の問題に対処するのを待っている多くの不可解なピットがあります!

テクニカルディレクターが残酷に言ったのも不思議ではありません。商品の量、注文取引、通貨計算などの項目を扱うときに、あえ​​て浮動小数点データ(double / float)を使用する人がいたら、直接行きましょう!

 

理由はどこですか?
分析する例として、最初の典型的な現象を取り上げましょう。

System.out.println(1f == 0.99999999f);
コードを直接使用して1と0.999999999を比較すると、実際にtrueが出力されます。

 

これは何を示していますか?これは、コンピューターがこれら2つの数字をまったく区別できないことを示しています。どうしてこれなの?

簡単に考えてみましょう。

入力された2つの浮動小数点数は、人間の目で見られる特定の値にすぎないことがわかっています。これらは通常理解できる10進数です。ただし、コンピューターの最下層は、計算時に10進法に従って計算されません。 。基本的な計算原理を学んだ人は、コンピューターの最下層が最終的に010100100100110011011のような0、1バイナリに基づいていることを知っています。

したがって、実際の状況を理解するには、これらの2つの10進浮動小数点数を2進空間に変換して確認する必要があります。

10進浮動小数点数を2進数に変換する方法と計算方法、これは基本的なコンピューターの16進変換の常識に属するべきだと思います。「コンピューター構成の原則」の同様のコースで学んだに違いありません。繰り返しません。ここで、結果を直接与えます(浮動小数点型に対応する精度であるIEEE 754単一精度32ビットに変換します)

1.0(10進数)
    ↓
00111111 10000000 00000000 00000000(2進数)
    ↓
0x3F800000(16進数)
0.99999999(10進数)
    ↓
00111111 10000000 00000000 00000000(2進数)
    ↓
0x3F800000(16進数)予想どおり
、これら2つの10進数の浮動小数点数の最下層表現は同じですが、==の判定結果がtrueを返すのも不思議ではありません。

ただし、1f == 0.9999999fによって返される結果は期待どおりです。falseが出力された場合は、状況を確認するためにバイナリモードにも変換します。

1.0(10進数)
    ↓
00111111 10000000 00000000 00000000(2進数)
    ↓
0x3F800000(16進数)
0.99999999(10進数)
    ↓
00111111 01111111 11111111 11111110(2進数)
    ↓
0x3F7FFFFE(16進数)
ああ、明らかに、2進数です。理由の結果。

では、なぜ0.9999999の基になるバイナリ表現は次のようになりますか:00111111 10000000 00000000 00000000?

これは、浮動小数点数1.0の2進表現が不明確ですか?

これは、浮動小数点数の精度についてです。

浮動小数点数の精度!
「コンピュータ構成の原則」を研究した人は、コンピュータでの浮動小数点数の格納方法がIEEE754浮動小数点数カウント標準に準拠していることを知っている必要があります。これは科学的記数法で次のように表すことができます。

 

記号(S)、順序部分(E)、仮数部分(M)の3次元の情報が与えられている限り、浮動小数点数の表現は完全に決定されるため、2つの浮動小数点数floatおよびdoubleは、メモリ内にあります。のストレージ構造は次のとおりです。

 

 

1.記号部(S)

0-正1-負

2.ステップコード部分(E)(指数部分):

浮動小数点数の場合、指数部分は正か負かを考慮して8ビットであるため、表現できる指数範囲は-127〜128です
。二重型浮動小数点数の場合、指数正または負のどちらでもよいことを考慮して、部分は11ビットであるため、表現できる指数の範囲は-1023〜1024です。3
マンティッサ部分(M):

浮動小数点数の精度は、仮数の桁数によって決まります。

浮動小数点型の浮動小数点数の場合、仮数部分は23桁で、10進数に変換されるのは2 ^ 23 = 8388608であるため、10進数の精度はわずか6〜7桁です。
二重型の浮動小数点数の場合、仮数部分はは52桁で、10進数に変換すると2 ^ 52 = 4503599627370496になるため、10進数の精度は15〜16桁になります。
したがって、上記の値0.99999999fの場合、float- typefloating-の精度範囲を超えていることは明らかです。ポイントデータ、および問題は避けられません。

精度の問題を解決する方法
では、商品の金額、取引額、通貨の計算などの高精度のシナリオが含まれる場合はどうなるでしょうか。

方法1:文字列または配列を使用して複数桁の問題を解決する

学校の採用でアルゴリズムの問​​題を解決したことがある人は、文字列または配列を使用して大きな数を表すことが典型的な問題解決のアイデアであることを知っておく必要があります。

たとえば、古典的なインタビューの質問:加算、減算、乗算、およびその他の2つの大きな任意の桁の演算を記述します。

現時点では、文字列や配列を使用してこのような大きな数を表現し、4つの算術演算の規則に従って特定の計算プロセスを手動でシミュレートできます。途中で、キャリー、ボロー、シンボルなど、本当に複雑なので、この記事では詳しく説明しません。

方法2:Javaの多数のクラスは良いことです

JDKは、浮動小数点数の計算精度をすでに考慮しているため、高精度の数値計算専用の多数のクラスを提供して、使用を容易にします。

前回の記事で述べたように、「私は最近Javaソースコードバーにアクセスしました」と述べたように、Javaの多数のクラスはjava.mathパッケージにあります。

 

ご覧のとおり、一般的に使用されているBigIntegerとBigDecimalは、高精度の数値計算を処理するための強力なツールです。

BigDecimal num3 = new BigDecimal(Double.toString(1.0f));
BigDecimal num4 = new BigDecimal(Double.toString(0.99999999f));
System.out.println(num3 == num4); // falseを出力
 
BigDecimalnum1 = new BigDecimal(Double.toString(0.2));
BigDecimal num2 = new BigDecimal(Double.toString(0.7));
 
//
System.out.println(num1.add(num2)); //印刷:0.9
 
//
システムを減算.out.println(num2.subtract(num1)); //印刷:0.5
 
//乗算
System.out.println(num1.multiply(num2)); //印刷:0.14
 
//除算
System.out.println(num2 .divide(num1)); // Print:3.5
もちろん、BigIntegerやBigDecimalのような大きな数値の計算効率は、元の型ほど効率的ではなく、コストも比較的高くなります。選択するかどうかは、次のようにする必要があります。実際のシーンに応じて評価されます。

 

おすすめ

転載: blog.csdn.net/guodashen007/article/details/106409903