CodeGym /Java Course /モジュール 1 /実数を扱う際の微妙な違い

実数を扱う際の微妙な違い

モジュール 1
レベル 3 , レッスン 2
使用可能

1. 実数の丸め

すでに説明したように、実数が変数に割り当てられる場合int、その実数は常に最も近い小さい整数に切り捨てられ、小数部分は単純に切り捨てられます。

しかし、小数をどちらかの方向に最も近い整数に四捨五入したり、さらには切り上げたりする必要がある状況を想像するのは簡単です。この場合どうしますか?

このような状況や多くの同様の状況に備えて、Java には、、およびメソッドをMath持つクラスがあります。round()ceil()floor()


Math.round()方法

このMath.round()メソッドは数値を最も近い整数に丸めます。

long x = Math.round(real_number)

ただし、ここには別のニュアンスがあります。このメソッドはlong( ではなくint) 整数を返します。実数は非常に大きくなる可能性があるため、Java の作成者は、Java で利用可能な最大の整数型である を使用することにしましたlong

したがって、プログラマが結果を変数に割り当てたい場合はint、(結果の数値が型に適合しない場合にint) データが失われる可能性を受け入れることをコンパイラに明示的に示す必要があります。

int x = (int) Math.round(real_number)

例:

声明 結果
int x = (int) Math.round(4.1);
4
int x = (int) Math.round(4.5);
5
int x = (int) Math.round(4.9);
5

Math.ceil()方法

このMath.ceil()メソッドは数値を整数に切り上げます。以下に例を示します。

声明 結果
int x = (int) Math.ceil(4.1);
5
int x = (int) Math.ceil(4.5);
5
int x = (int) Math.ceil(4.9);
5

Math.floor()方法

このMath.floor()メソッドは数値を整数に切り捨てます。以下に例を示します。

声明 結果
int x = (int) Math.floor(4.1);
4
int x = (int) Math.floor(4.5);
4
int x = (int) Math.floor(4.9);
4

もちろん、数値を整数に切り捨てる場合は単純に型キャスト演算子を使用する方が簡単です。(int)

声明 結果
int x = (int) 4.9
4

これらの名前を覚えるのが難しい場合は、短い英語のレッスンが役に立ちます。

  • Math数学を意味します
  • Round丸いという意味
  • Ceiling天井を意味します
  • Floor床を意味します

2. 浮動小数点数の構造

この型はから までdoubleの範囲の値を格納できます。(型と比較して) この膨大な値の範囲は、型 (および) が整数型とは完全に異なる内部構造を持っているという事実によって説明されます。内部的に、型はその値を 2 つの数値としてエンコードします。1 つ目は仮数と呼ばれ 2 つ目は指数と呼ばれます。-1.7*10308+1.7*10308intdoublefloatdouble

数値を取得し123456789、それをdouble変数に保存するとします。これを行うと、数値は に変換され、型は内部的に 2 つの数値 (と )を格納します。仮数 (「数値の有効部分」または仮数) は赤色で強調表示され、指数は青色で強調表示されます。1.23456789*108double234567898

このアプローチにより、非常に大きな数値と非常に小さな数値の両方を保存することが可能になります。ただし、数値の表現は 8 バイト (64 ビット) に制限されており、ビットの一部は指数(および仮数の符号と指数の符号) を格納するために使用されるため、仮数を表すために使用できる最大桁数は次のようになりますは15です。

これは、実数がどのように構成されているかを非常に単純化して説明したものです。


3. 実数を扱う際の精度の低下

実数を扱うときは、実数は 正確ではないことに常に留意してください。10 進数から 2 進数に変換する場合、常に丸め誤差変換エラーが発生する可能性があります。さらに、最も一般的なエラーの原因は、根本的に異なるスケールで数値を加算/減算するときに精度が失われることです。

この最後の事実は、初心者プログラマにとっては少し驚くべきことです。

から引くと、 が得られます。1/109109109

根本的に異なるスケールで数値を引き算する 説明
 1000000000.000000000;
-         0.000000001;
 1000000000.000000000;
2 番目の数値は非常に小さいため、仮数 (灰色で強調表示されている) が無視されます。有効数字 15 桁がオレンジ色で強調表示されます。

何と言っても、プログラミングは数学と同じではありません。


4. 実数を比較する際の落とし穴

プログラマーが実数を比較するときに、別の危険が待ち構えています。実数を扱う場合、丸め誤差が累積する可能性があるため、この問題が発生します。その結果、実数が等しいと期待されているにもかかわらず、実際は等しくない状況が発生します。またはその逆: 数値は異なることが予想されますが、数値は等しいです。

例:

声明 説明
double a = 1000000000.0;
double b = 0.000000001;
double c = a - b;
変数の値はa になります1000000000.0
変数の値はc になります1000000000.0
(変数の数値b が小さすぎます)

上の例では、aと はc等しくないはずですが、等しくなります。

あるいは、別の例を見てみましょう。

声明 説明
double a = 1.00000000000000001;
double b = 1.00000000000000002;
変数の値は次a のようになります。1.0
変数の値は次b のようになります。1.0

5. 興味深い事実strictfp

Java には、他のプログラミング言語には見られない特別なキーワード ( strict floating point )strictfpあり ますそして、なぜそれが必要なのか知っていますか? 浮動小数点数を使用した演算の精度が低下します。それがどのようにして生まれたのかは次のとおりです。

Java の作成者:
私たちは、Java が非常に普及し、できるだけ多くのデバイスで Java プログラムが実行されることを本当に望んでいます。そこで私たちは、Java マシンの仕様に、すべてのプログラムがすべてのタイプのデバイスで同じ方法で実行される必要があることを確認しました。
Intel プロセッサのメーカー:
こんにちは、みなさん!私たちはプロセッサを改良し、プロセッサ内ですべての実数が 8 バイトではなく 10 バイトを使用して表現されるようになりました。バイト数が多いほど、有効桁数が多くなります。どういう意味ですか?それは正しい!これで、科学的な計算がさらに正確になります。
科学者と超精密計算に携わるすべての皆様:
いいね!素晴らしい。すばらしいニュースです!
Java の作成者:
いやいやいや、皆さん!すべての Java プログラムはすべてのデバイスで同じように実行する必要があることはすでに説明しました。Intel プロセッサ内で 10 バイトの実数を使用する機能を強制的に無効にします。
これでまたすべてがうまくいきました!私たちに感謝しないでください。
科学者と超精密計算に携わるすべての皆様:
完全に狂ってしまったのか?早くすべてを元通りに戻してください!
Java の作成者:
皆さん、これはあなた自身の利益のためです!想像してみてください。すべての Java プログラムがすべてのデバイスで同じように実行されます。とてもクールですね!
科学者と超精密計算に携わるすべての皆様:
いや、全然かっこよくないよ。すぐにすべてを元の状態に戻します。それとも Java をどこに置くか知っていますか?
Java の作成者:
ふーむ。なぜすぐにそう言わなかったのですか?もちろん元に戻します。
最新のプロセッサーのすべての機能を使用できるように復元しました。
ちなみに…strictfp言語にも特別にキーワードを追加しました。これを関数名の前に記述すると、その関数内の実数を含むすべての操作がすべてのデバイスで同様に不正になります。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION