Java不是确定性的吗?

mibutec:

我已经用Java编写了一些捕食者-猎物模拟。即使规则很复杂并且最终陷入混乱的系统中,使用的技术也很简单:

  • 基本数据类型的算术和决策
  • 没有外部库
  • 不包括外部系统
  • 没有并发发生
  • 不使用当前时间或日期

因此,我认为在使用相同参数初始化系统时,它应该输出相同的结果,但是没有,我想知道为什么。

关于这一点的一些想法:我的应用程序使用Randoms,但是对于该测试,我使用给定值将它们全部初始化,因此,根据我的理解,它们应为每次运行创建相同顺序的相同输出。

我正在遍历Sets,并且我知道a Set的迭代顺序未定义。但是我看不出为什么以Set相同顺序填充相同值的a在几次运行中表现出不同的任何原因可以?

我正在用很多floats。我总是怀疑1 + 1 = 1.9999999999725的数据类型,但是即使我的行为对我来说很奇怪,也应该总是一样。是不是

垃圾回收不是确定性的,但是只要我不依赖析构函数,我就应该是安全的。

如上所述,取决于实际使用时间,没有并发和数据类型。

我无法在一个简单的示例中重现该行为。但是遍历我的代码,我看不到任何无法预测的东西。那么我上面的任何假设是错误的吗?有什么想法我可能会错过吗?

这是验证我的假设的测试:

public static void main(String[] args) {
    Random r = new Random(1);
    Set<Float> s = new HashSet<Float>();
    for (int i = 0; i < 1000000; i++) {
        s.add(r.nextFloat());
    }

    float ret = 1;
    int cnt = 0;
    for (Float f : s) {
        float multiply = 0.3f;
        if (cnt++ % 2 == 0) {
            multiply = 0.7f;
        }
        float f2 = (f * multiply);
        ret += f2;
    }

    System.out.println(ret);
}

对我来说,结果总是242455.25。

斯蒂芬·C:

您可以使用Java编写确定性程序。您只需要消除不确定性的可能来源。

在不查看您的实际代码以及该确定性的具体证据的情况下,很难知道是什么导致了不确定性。

有许多库方法可能是不确定性行为的来源……这取决于您如何使用它们。

例如,Object.hashcode()(第一次在实例上调用它)返回的值是不确定的。这渗入到任何使用哈希的库中。它绝对可以影响它的要素的顺序HashSetHashMap当你重复他们返回......如果元素类没有重载hashcode()

随机数生成器可能是确定性的,也可能不是确定性的。如果它们是伪随机的,并且使用固定的种子进行初始化,则每个序列产生的数字序列将是确定的。

浮点算法应该是确定性的。对于算术表达式的任何(固定)输入集,结果应始终相同。(我不确定JLS是否可以保证浮点运算的确定性,但是如果它在实践中发生,那就太奇怪了。就像在...上运行的是损坏的硬件。)


FOLLOWUP ... on strictfp和不确定性。

根据JLS 15.4

“在不受FP限制的表达式中,为实现提供了一定的回旋余地,以使用扩展的指数范围来表示中间结果;大致而言,最终结果是,在以下情况下,计算可能会产生“正确答案”独占使用浮点值集或双精度值集可能会导致上溢或下溢。”

这并不能完全说明该实现在非FP严格表达式中有多少 “余地”。但是,我曾经以为,这种余地不会扩展到允许非确定性行为。我以为在特定平台上的JIT编译器将始终为同一表达式生成等效的本机代码,并且该代码将是确定性的。(除非硬件本身具有非确定性的浮点数,否则我看不到任何非确定性的原因。)其他不确定性的可能来源可能是JIT编译和解释的代码的行为可能不同。但坦率地说,我认为这将是“坚果”……我想我们已经听说过。

因此,尽管非FP严格的表达评估在理论上可能是不确定的,但我认为我们应该轻视它……除非有明确的证据表明它在实践中发生。

(请注意,我说的是真正的不确定性,而不是平台差异。)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章