【问题标题】:How do I prove that my Double.toString algorithm is correct for all values? [closed]如何证明我的 Double.toString 算法对所有值都是正确的? [关闭]
【发布时间】:2017-07-24 03:32:20
【问题描述】:

我正在优化 Java 的慢速 Double.toString 算法。我已经成功地重写了 Float.toString(速度提高了 400% 以上)。测试 Float.toString 的算法很容易,因为我可以在煮鸡蛋的时间里迭代 throw 所有可能的值(从 Integer.MIN_VALUE 到 Integer.MAX_VALUE)。

但是,以同样的方式测试 Double.toString 的准确性需要我从 Long.MIN_VALUE 迭代到 Long.MAX_VALUE。我可以在所有线程上开始这个测试并在我的余生中运行它,我敢打赌它不会完成。

需要明确的是,当我测试这个算法时,我只是简单地使用我的结果字符串并根据 java.lang.Double.toString(double d) 的结果调用 String.equals。如果它们匹配,我会转到下一个值。

我对算法的改进主要涉及消除不必要的精度。在计算 Double.toString 时,它使用一种特殊的 BigInteger 类来执行此操作。但是,我发现通过修剪无关紧要的位,我仍然可以获得相同的结果,并显着提高性能。

我认为我可以将所有值修剪到不超过 128 位(用偏移量替换修剪后的位)而不会失败我的测试,但是如何在不迭代每个值的情况下证明这一点?

我想我要问的是:原始算法的创建者如何在不测试所有可能的输入的情况下绝对确定他们的算法是正确的?

【问题讨论】:

  • and I bet it wouldn't finish 是对的,因为浮点数代表实数,在 0 和 1 之间实际上有 无限 个数。更准确地说,在任意两个实数之间存在无限数量的其他实数。
  • 这种情况的另一个结果是浮点数永远无法正确表示所有实数,只能表示适合它的实数。只有 non-significant 位,如果存储在浮点数中的当前值在 0 之后只有很少的数字,它们适合在那里并且可以二进制表示。例如 0.1 不能以二进制形式表示,因为它是 1/10。
  • 出于实际目的,在 Java 中,Double.toString 有 2^64 个可能的输入。其中一些解析为相同的值(NaN)。一半是阴性的。有负无穷和正无穷,负零和正零。一些可以使用稍微修改的 Long.toString 算法来解决。然而,大多数是通过找到值 b 和 s 来计算的,使得 (b/s)*10^decExp = double 值。十进制指数是根据需要估计和调整的。
  • @JohnSmith 您的 cmets 相互矛盾。您可以轻松(尽管这很耗时)迭代所有可能的浮点数,因为 它们并不代表每个实数,正如您在第一条评论中所说的那样;您的第二个是准确的,因为有限精度意味着一些“附近”数字聚集在一起,并且二进制表示具有无限位数的数字被截断。有限的精度意味着是的,可以迭代每个浮点数,只要从 32 0 表示的那个开始,然后像二进制整数一样递增它直到 32 1。

标签: java double tostring floating


【解决方案1】:

我不...完全确定他们做到了。

您可以查看为Double#toString 编写的the OpenJDK 8OpenJDK 9 测试,并没有得到太多...满意:

/*
 * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 4428022
 * @summary Tests for Double.toString
 * @author Andrew Haley <aph@redhat.com>
 */

public class ToString {

    public static void main(String args[]) {
      if (!Double.toString(0.001).equals("0.001"))
          throw new RuntimeException("Double.toString(0.001) is not \"0.001\"");
      if (!Double.toString(0.002).equals("0.002"))
          throw new RuntimeException("Double.toString(0.001) is not \"0.002\"");
    }
}

实际上,他们所做的只是测试两个案例;如果toString 方法正确识别"0.001""0.002" 为满意返回。

可能与浮点数在处理这类分数方面臭名昭著的事实有关,这对于任何试图将双精度数转换为字符串的东西都是一个不错的测试以这种方式;很多情况下,他们只是创建了一个测试来涵盖基础知识。

随心所欲;我鼓励你对你想要测试的其他东西多加思考。由此看来,似乎只捕获了边缘情况;您可能希望通过自己的优化来扩展它。

虽然......将这些测试(以更好的方式,请注意)添加到您自己的套件中也不是最糟糕的主意。自 09 年以来,它们没有改变。

【讨论】:

  • 哇....这就是他们测试的全部内容?我可以向您保证,还有很多有效的测试用例。例如,我知道算法中(至少)有 4 或 5 个主要子情况,它们处理计算的方式完全不同。如何测试所有这些情况?知道它经过了多少测试并不是特别能增强信心。
  • 嗯,不,我不认为在看到这确实是 it 来测试Double#toString 之后,你的信心会特别高。回答您的后续问题:直接测试这些组件。考虑每个组件旨在处理的每个案例,并围绕这些案例进行测试。
猜你喜欢
  • 1970-01-01
  • 2022-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-23
  • 2013-03-11
相关资源
最近更新 更多