2

注:質問はまだ完全には回答されていません!この質問は、浮動小数点部分の切り捨ての問題を扱っていません!!!

Javaには、次の単純なコードがあります。

double sum = 0.0;
for(int i = 1; i <= n; i++){
    sum += 1.0/n
}
System.out.println("Sum should be: 1");
System.out.println("The result is: " + sum);

ここで、nは任意の整数です。7,9のような数値の場合、sumの期待値は、sumの最後の桁に差があり、結果は0.999999999998か何かになりますが、3を使用した場合の出力は1.0です。

1/3を3回追加すると、1に近い数値が期待されますが、正確に1.0になります。

なんで?

4

4 に答える 4

3

これは、除算が整数で行われるためです。

1/nn>1の場合は常に0になります。

したがって、常に合計= 0 + 1/1 + 0 +0..になります。

で試してみてください1.0 / n

于 2013-03-22T16:47:48.200 に答える
2

1/3を3回追加すると、1に近い数値が期待されますが、正確に1.0になります。

実際、プログラミングの経験に汚染されていない普通の人は、n * 1 / nが1に等しいと期待しますが、ここでは普通ではありません。

私はあなたの問題を正確に再現することはできません、私は得ます

groovy:000> def foo(n) {
groovy:001>   sum = 0.0
groovy:002>   for (int i = 0; i < n; i++) {
groovy:003>     sum += 1.0 / n
groovy:004>   }
groovy:005>   sum
groovy:006> }
===> true
groovy:000> foo(3)
===> 0.9999999999

ここには2つの問題があるかもしれませんが、少なくともそれらに注意する必要があります。

1つは、doubleは正確ではなく、一部の値を正確に表すことができないことです。また、少しずれていることを期待する必要があります。目標は100%の精度ではなく、エラーを許容範囲内に保つことです。(Peter Lawreyが、ダブルスに関する興味深い記事をチェックしておくとよいでしょう。)それがうまくいかない場合は、ダブルスを避けたいと思うでしょう。多くの用途では、BigDecimalで十分です。質問の分割の問題が正確な答えを与えるライブラリが必要な場合は、この質問の答えを確認してください。

もう1つの問題は、System.out.printlnがdoubleの正確な値を教えてくれないことです。これは、少し曖昧です。次のような行を追加した場合:

System.out.println(new java.math.BigDecimal(sum));

次に、ダブルに含まれるものの正確なビューを取得します。

于 2013-03-22T17:33:25.270 に答える
1

あなたが何を問題と考えているのかわからないので、これが物事を明確にするのに役立つかどうかはわかりません。

これは、前に提案したように、BigDecimalを使用して中間回答の値を表示するテストプログラムです。最後のステップで、1.0 / 3の3番目のコピーを2つのコピーの合計に追加すると、正確な答えは1.0とそれよりも2倍低い値の中間になります。そのような状況では、四捨五入のルールは1.0を選択します。

それを考えると、質問のタイトルと矛盾して、1.0に丸めるべきだと思います。

テストプログラム:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    final double oneThirdD = 1.0/3;
    final BigDecimal oneThirdBD = new BigDecimal(oneThirdD);
    final double twoThirdsD = oneThirdD + oneThirdD;
    final BigDecimal twoThirdsBD = new BigDecimal(twoThirdsD);
    final BigDecimal exact = twoThirdsBD.add(oneThirdBD);
    final double nextLowerD = Math.nextAfter(1.0, 0);
    final BigDecimal nextLowerBD = new BigDecimal(nextLowerD);
    System.out.println("1.0/3: "+oneThirdBD);
    System.out.println("1.0/3+1.0/3: "+twoThirdsBD);
    System.out.println("Exact sum: "+exact);
    System.out.println("Rounding error rounding up to 1.0: "+BigDecimal.ONE.subtract(exact));
    System.out.println("Largest double that is less than 1.0: "+nextLowerBD);
    System.out.println("Rounding error rounding down to next lower double: "+exact.subtract(nextLowerBD));
  }
}

出力:

1.0/3: 0.333333333333333314829616256247390992939472198486328125
1.0/3+1.0/3: 0.66666666666666662965923251249478198587894439697265625
Exact sum: 0.999999999999999944488848768742172978818416595458984375
Rounding error rounding up to 1.0: 5.5511151231257827021181583404541015625E-17
Largest double that is less than 1.0: 0.99999999999999988897769753748434595763683319091796875
Rounding error rounding down to next lower double: 5.5511151231257827021181583404541015625E-17
于 2013-03-23T04:05:31.110 に答える
0

intをintで割ると、常に別のintが生成されます。現在、intには数値の小数部分を格納する場所がないため、破棄されます。丸めずに廃棄されることに注意してください。

したがって、1/3 = 0.3333333となり、小数部は破棄され、0になります。

数値をdoubleとして指定すると(小数点を含める、例:1または1.0)、結果はdoubleになり(javaはintをdoubleに自動的に変換するため)、小数部分は保持されます。

更新された質問では、iを1.0に設定していますが、iはまだintです。1.0が1に切り捨てられるように、さらに計算するために、それはまだintです。iのタイプもdoubleに変更する必要があります。そうしないと、コードに違いはありません。

または、sum + = 1.0/nを使用することもできます

これは、計算を実行する前にnをdoubleに変換する効果があります

于 2013-03-22T16:51:21.193 に答える