このメソッドは「true」を返します。なんで ?
public static boolean f() {
double val = Double.MAX_VALUE/10;
double save = val;
for (int i = 1; i < 1000; i++) {
val -= i;
}
return (val == save);
}
このメソッドは「true」を返します。なんで ?
public static boolean f() {
double val = Double.MAX_VALUE/10;
double save = val;
for (int i = 1; i < 1000; i++) {
val -= i;
}
return (val == save);
}
大きな値から非常に小さな値(1000未満)を減算しています。小さい値は大きい値よりもはるかに小さいため、理論結果に最も近い表現可能な値は元の値のままです。
基本的に、これは浮動小数点数の動作の結果です。
仮数に有効数字5桁と、0〜1000の範囲の指数のみを格納する10進浮動小数点型(簡単にするため)があるとします。
あなたの例は、10 999-1000と書くようなものです...有効数字5桁に丸めた場合、その結果がどうなるかを考えてください。はい、正確な結果は99999 ..... 9000(999桁)ですが、有効数字5桁の値しか表現できない場合、最も近い結果は10999になります。
Double.MAX_VALUE / 10に設定val
すると、ほぼ等しい値に設定され1.7976931348623158 * 10^307
ます。そこから1000のような値を差し引くと、二重表現の精度が必要になりますが、これは不可能であるため、基本的にはval
変更されません。
必要に応じて、BigDecimal
の代わりに使用できますdouble
。
Double.MAX_VALUE
非常に大きいため、JVMはそれとの違いを認識しませんDouble.MAX_VALUE-1000
結果から「1.9958403095347198E292」よりも小さい数を引くと、Double.MAV_VALUE
まだDouble.MAX_VALUE
です。
System.out.println(
new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(
Double.MAX_VALUE - 2.E291) )
);
System.out.println(
new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(
Double.MAX_VALUE - 2.E292) )
);
Ouptup:
true
false
Double.MAX_VALUE
は1または1000に比べて膨大な数です。Double.MAX_VALUE-1
通常、はに等しくなりDouble.MAX_VALUE
ます。したがって、1または1000をに減算しても、コードは大まかに何もしませんDouble.MAX_VALUE/10
。常に覚えておいてください:
double
sまたはfloat
sは実数の単なる近似であり、実数間で均等に分散されていない単なる有理数です。double
近くにないsまたはsの間で非常に注意深く算術演算子を使用する必要float
があります(このような他の多くのルールがあります...)double
sを使用しないでください。またはfloat
、任意精度が必要な場合は、doubleには、試行している計算を実行するのに十分な精度がありません。したがって、結果は初期値と同じになります。
==
オペレーターとは何の関係もありません。
val
は大きな数であり、そこから減算1
(または1000
)すると、結果を値として正しく表現できませんdouble
。無制限の数を表すために限られた数のビットしかないため、この数の表現はx
同じx-1
です。double
浮動小数点計算の結果は、正確な答えに最も近い表現可能な値です。このプログラム:
public class Test {
public static void main(String[] args) throws Exception {
double val = Double.MAX_VALUE/10;
System.out.println(val);
System.out.println(Math.nextAfter(val, 0));
}
}
プリント:
1.7976931348623158E307
1.7976931348623155E307
これらの数値の最初は、元の値です。2番目はそれよりも小さい最大のダブルです。
1.7976931348623158E307から1000を引くと、正確な答えはこれら2つの数値の間にありますが、1.7976931348623155E307よりも1.7976931348623158E307に非常に近いため、結果は1.7976931348623155E307に丸められ、valは変更されません。
double
は浮動小数点数値型であり、数値を概算する方法であるためです。浮動小数点表現は数値をエンコードするため、通常よりもはるかに大きい数値または小さい数値を格納できます。ただし、指定されたスペースですべての数値を表すことができるわけではないため、複数の数値は同じ浮動小数点値に丸められます。
簡単な例として、通常は-10から10しか格納できない小さなスペースに、-1000から1000の範囲の値を格納できるようにしたい場合があります。したがって、すべての値を1000の位に丸めることができます。 -1000はとしてエンコードされ-10
、-900はとしてエンコードされ-9
、1000はとしてエンコードされ10
ます。しかし、-999を保存したい場合はどうなりますか?エンコードできる最も近い値は-1000であるため、-999を-1000と同じ値としてエンコードする必要があります-10
。
実際には、浮動小数点スキームは上記の例よりもはるかに複雑ですが、概念は似ています。数値の浮動小数点表現は、可能なすべての数値の一部しか表現できないため、スキームの一部として表現できない数値がある場合は、最も近い表現可能な値に丸める必要があります。
コードでは、1000以内のすべての値がDouble.MAX_VALUE / 10
自動的にに丸められます。Double.MAX_VALUE / 10
これが、コンピューターが考える理由(Double.MAX_VALUE / 10) - 1000 == Double.MAX_VALUE / 10
です。