12

我用 Java 编写了一个小捕食者-猎物模拟。即使规则非常复杂并最终进入一个混乱的系统,使用的技术也很简单:

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

所以我想当用相同的参数初始化系统时,它应该输出相同的结果,但事实并非如此,我想知道为什么。

对此的一些想法:我的应用程序使用Randoms,但是对于那个测试,我用给定的值初始化它们,所以在我的理解中,它们应该为每次运行以相同的顺序创建相同的输出。

我正在迭代Sets,并且我知道Set迭代 a 的顺序没有定义。但是我看不出有任何理由说明以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。

4

2 回答 2

20

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

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

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

例如,Object.hashcode()(第一次在实例上调用它)返回的值是不确定的。这渗透到任何使用散列的库。它肯定会影响 a HashSetor的元素HashMap在您迭代它们时返回的顺序......如果元素类没有覆盖hashcode()

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

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


跟进...关于strictfp和非确定性。

根据JLS 15.4

“在非 FP 严格的表达式中,允许实现使用扩展指数范围来表示中间结果;净效应,粗略地说,是在以下情况下计算可能会产生“正确答案”独占使用浮点值集或双精度值集可能会导致上溢或下溢。”

这并不能准确说明实现在非 FP-strict 表达式中有多少“余地”。但是,我原以为这种余地不会扩展到允许非确定性行为。我曾认为特定平台上的 JIT 编译器总是会为相同的表达式生成等效的本机代码,并且该代码将是确定性的。(我看不出任何非确定性的原因......除非硬件本身具有非确定性浮点。)非确定性的另一个可能来源可能是 JIT 编译和解释代码的行为可能不同。但坦率地说,我认为允许这种情况发生是“疯狂的”……而且我认为我们已经听说过。

因此,虽然非 FP 严格的表达式评估在理论上可能是不确定的,但我认为我们应该忽略这一点……除非有明确的证据表明它在实践中发生。

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

于 2013-07-13T02:06:49.857 回答
11

我正在遍历 Set,并且我知道 Set 的迭代顺序没有定义。但是我看不出有任何理由说明为什么以相同顺序填充具有相同值的 Set 在多次运行中表现得不同。可以?

它可以。该实现可以自由使用,例如,对象在内存中的位置作为底层哈希表的键。这取决于垃圾收集运行的时间。

于 2013-07-13T02:08:51.660 回答