【问题标题】:Java For-loop changes numeric result when changing type of loop variable更改循环变量的类型时,Java For-loop 会更改数值结果
【发布时间】:2016-04-09 04:52:35
【问题描述】:

我写了一个程序用莱布尼茨公式计算PI数:

[

我写了一个初始化类型为“int”的for循环,循环工作正常,但是当我将初始化类型更改为“long”时,结果发生了变化。这只发生在循环次数超过十亿时。这使得“int-loop”计算 PI 比“long-loop”更准确。我不知道为什么会这样。请帮助我理解这个问题。谢谢!这是我的代码。

public static void main(String[] args) {
    double result1 = 0;
    double result2 = 0;
    double sign = 1;

    for (int i = 0; i <= 1607702095; i++) {
        result1 += sign/(2 * i + 1);
        sign *= -1;
    }
    sign = 1;

    for (long j = 0; j <= 1607702095; j++) {
        result2 += sign/(2 * j + 1);
        sign *= -1;
    }

    System.out.println("result1  " + result1 * 4);
    System.out.println("result2  " + result2 * 4);
    System.out.println("pi       " + Math.PI);
}

结果是:

result1  3.141592653576877
result2  3.1415926529660116
pi       3.141592653589793

【问题讨论】:

  • @programer310 那太好了:)。您现在看到了一个好问题的样子,并且您可以看到,其他用户通过投票表示赞赏。顺便说一句,当你点击edit 时,你可以看到它是如何格式化的(如果你好奇的话)。
  • 如您所知,存在整数溢出。但也要小心初始化:sign 必须在第二个循环之前重置为 1
  • 现在向您提出一个有趣的问题。假设您以几种不同的顺序进行计算。你从大的东西开始,然后向它们添加越来越小的东西。如果你从小端开始,逐渐添加越来越大的东西,你会得到不同的结果吗?如果你先做所有的负面,然后再做所有的正面呢?在“真实”算术中,执行一系列加法的顺序无关紧要。在Java中还有什么重要的吗?如果有区别,你能推断出为什么有区别吗?
  • @edc65 - 很好的一点 - 我试图弄清楚long 循环如何可能int 更准确。几乎可以肯定,因为答案较低,如果您的第一个(最大值)循环估计向下而不是向上,则可以预期。
  • @edc65 - 感谢您的评论,我根据您的建议编辑了我的代码。实际上结果并没有改变,因为在第一个循环之后,“sign”的值已经等于 1。

标签: java numeric


【解决方案1】:

2 * ii 接近循环结束时,将溢出int 的最大值,即2147483647 em>。

使用 long 该操作不会溢出。

正确的过程是使用长类型。可能是因为一些奇怪的行为在正确的 PI 周围添加和删除了值,所以溢出会暂时计算为更接近正确 PI 的值。

我认为更改 for 循环的少数值的限制会将最终结果更改为离正确 PI 更远的值。

【讨论】:

    【解决方案2】:

    你有整数溢出

    有符号整数的最大容量为 (2^31)-1,即 2,147,483,647。

    (1,607,702,095 * 2) 是 3215404190,大于 2,147,483,647。

    当您将 i 更改为 long 时,您会将 i 的容量增加到 (2^63)-1。

    【讨论】:

      【解决方案3】:

      注意到每个人都在指出整数溢出,但您可能需要一个解决方案。 (如果您已经有,请忽略以下内容:))

      如果代码的(2 * i + 1) 部分溢出,您应该将for 循环中的i 最大化为(Integer.MAX_VALUE / 2 - 1),这会导致:

      for (int i = 0; i <= (Integer.MAX_VALUE / 2 - 1); i++) {
          result1 += sign/(2 * i + 1);
          sign *= -1;
      }
      

      您也可以使用 (Long.MAX_VALUE / 2 - 1) 对较长的部分执行此操作,但它会运行很长时间。

      【讨论】:

        【解决方案4】:

        因为你在行溢出

        result1 += sign/(2 * i + 1);
        

        2*i 的值与最大整数值交叉的地方

        int 范围是-2,147,483,648 to 2,147,483,647,但是当您为更大的价值使用2*i 时,它会跨越该范围。

        最好坚持使用long,这样可以得到正确的输出。

        【讨论】:

          【解决方案5】:

          实际上,当i 足够大时,您的第一个循环会在(2 * i + 1) 的计算中出现int 溢出,所以我不会依赖它的输出。

          另一方面,第二个循环产生更正确的输出,因为 (2 * j + 1) 不会溢出,因为它执行 long 乘法。

          这使得“int - loop”计算PI比“long - loop”更准确

          这可能只是一个巧合,因为 int 循环中的计算会溢出。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-10-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-05
            相关资源
            最近更新 更多