【问题标题】:generate ill-conditioned data for testing floating point summation生成病态数据以测试浮点求和
【发布时间】:2011-07-06 20:13:36
【问题描述】:

我在 Java 中实现了 Kahan floating point summation algorithm。我想将它与 Java 中的内置浮点加法和 Mathematica 中的无限精度加法进行比较。但是我拥有的数据集不适合测试,因为数字彼此接近。 (条件数~= 1)

在我的数据集上运行 Kahan 得到的结果与内置的 + 几乎相同。

谁能建议如何生成大量可能导致严重舍入错误的数据?

【问题讨论】:

  • 看看“路由错误”部分的this ibm site。基本上,您需要生成一些不能表示为具有负指数的二进制数的数字。例如,0.5 = 2^-1,但 0.1 不是。这个来自英特尔的site 也给出了一个很好的理论。而你想让你的java由于舍入问题而崩溃,take a look at this site 关于使用数字 2.2250738585072012e-308 ,

标签: java floating-point


【解决方案1】:

但是我的数据集不适合测试,因为数字很接近。

听起来您已经知道问题所在了。开始吧=)

你会想要一些东西:

  • 数量级差异很大的数字,因此较小数字的大部分精度都会因简单求和而丢失。
  • 符号不同且大小几乎相等(或相等)的数字,从而发生灾难性的抵消。
  • 设置了一些低位的数字,以增加四舍五入的效果。

为了让你开始,你可以尝试一些简单的三项求和,它应该清楚地显示效果:

1.0 + 1.0e-20 - 1.0

用简单的求和评估,这将得到0.0;显然不正确。您还可以查看表格的总和:

a0 + a1 + a2 + ... + an - b

其中ba0 + ... + an 天真评估的总和。

【讨论】:

    【解决方案2】:

    您想要一堆高精度数字吗?试试这个:

    double[] nums = new double[SIZE];
    for (int i = 0; i < SIZE; i++)
        nums[i] = Math.rand();
    

    【讨论】:

      【解决方案3】:

      我们是在谈论数字对还是序列?

      如果成对,两个数字都从 1 开始,然后在每次迭代中将一个除以 3,将另一个乘以 3。计算这些对的理论总和很容易,您会得到一大堆舍入误差。 (一些来自除法,一些来自加法。如果您不希望除法错误,请使用 2 而不是 3。)

      【讨论】:

        【解决方案4】:

        通过实验,我发现了以下模式:

        public static void main(String[] args) {
            System.out.println(1.0 / 3 - 0.01 / 3);
        
            System.out.println(1.0 / 7 - 0.01 / 7);
        
            System.out.println(1.0 / 9 - 0.001 / 9);
        }
        

        我已经减去了素数的近似负幂(它不应该具有二进制形式的精确表示)。但是,在某些情况下,这样的表达式会正确计算,例如

            System.out.println(1.0 / 9 - 0.01 / 9);
        

        您可以通过迭代减数的幂并在乘以适当的值未产生整数时停止来自动化此方法,例如:

            System.out.println((1.0 / 9 - 0.001 / 9) * 9000);
            if (1000 - (1.0 / 9 - 0.001 / 9) * 9000 > 1.0)
                System.out.println("Found it!");
        

        【讨论】:

          【解决方案5】:

          Scalacheck 可能适合您。这是一个简短的示例:

          cat DoubleSpecification.scala
          import org.scalacheck._
          
          object DoubleSpecification extends Properties ("Doubles") {
                  /* 
                      (a/1000 + b/1000) = (a+b) / 1000
                      (a/x    + b/x   ) = (a+b) / x
                  */
                  property ("distributive") = Prop.forAll { (a: Int, b: Int, c: Int) => 
                      (c == 0 || a*1.0/c + b*1.0/c == (a+b) * 1.0 / c)            }
          }       
          
          object Runner { 
              def main (args: Array[String]) {
                  DoubleSpecification.check
                  println ("...done")
              }
          }
          

          要运行它,您需要 scala 和 schalacheck-jar。我用的是 2.8 版(不用我说,你的 c-path 会有所不同):

           scalac -cp /opt/scala/lib/scalacheck.jar:. DoubleSpecification.scala 
           scala -cp /opt/scala/lib/scalacheck.jar:. DoubleSpecification
          ! Doubles.distributive: Falsified after 6 passed tests.                       
          > ARG_0: 28 (orig arg: 1030341)
          > ARG_1: 9 (orig arg: 2147483647)
          > ARG_2: 5
          

          Scalacheck 采用一些随机值(orig args)并尝试简化这些值,如果测试失败,以便找到简单的示例。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-01-04
            • 1970-01-01
            • 1970-01-01
            • 2017-03-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多