为什么Java HashMap复制构造函数会影响浮点精度?

pmc*_*255 3 java floating-point copy-constructor

我有一些代码计算浮点数映射的线性组合,并遇到了使用复制构造函数的有趣副作用.

如果我计算两个映射中的值的线性组合并将其与使用这些映射的两个副本中的值计算的线性组合进行比较,则由于出现的情况,计算实际上具有略微不同(在10 ^ -7附近)的结果浮点精度.

为什么会这样?

这是一些示例代码:

import java.util.*;

public class WTF {
    public static void main(String[] args) {
        Random rand = new Random();

        for (int c = 0; c < 1000; c++) {
            Map<String, Float> weights = new HashMap<String, Float>();
            Map<String, Float> values = new HashMap<String, Float>();

            for (int j = 0; j < 10; j++) {
                weights.put("sig" + j, Float.valueOf(rand.nextFloat()));
                values.put("sig" + j, Float.valueOf(rand.nextFloat()));
            }

            Map<String, Float> weightsCopy = new HashMap<String, Float>(weights);
            Map<String, Float> valuesCopy = new HashMap<String, Float>(values);

            float score1 = getScore(weights, values);
            float score2 = getScore(weightsCopy, valuesCopy);

            if (score1 != score2) {
                System.out.println(score1-score2);
            }
        }
    }

    public static float getScore(Map<String, Float> weights, Map<String, Float> values) {
        float score = 0.0f;
        for (String name : weights.keySet()) {
            Float weight = weights.get(name);
            Float value = values.get(name);
            score += weight.floatValue() * value.floatValue();
        }
        return score;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

同样的问题也适用于该putAll操作.使用它来"复制" HashMap导致相同的浮点精度问题.

Lou*_*man 5

迭代顺序正在从原始地图变为副本,因为它正在重建哈希表(可能具有不同的大小).

在舍入的差异来自于事实,*+在浮动不是可交换/关联,并根据你是否你会得到不同的舍入误差a * (b * c)(a * c) * b(a * b) * c.由于条目和键的顺序在原件和副本之间发生变化,因此结果中的舍入差异很小.

如果您使用LinkedHashMap而不是HashMap确保保留迭代顺序,则每次都应该得到完全相同的结果.(我在我的机器上证实了这一点.)


Fau*_*aux 5

地图中的顺序正在发生变化,导致操作以不同的顺序运行.简单计算的输出变化示例(注意翻转的d和e):

class WTF {
    public static void main(String[] args) {
        final float a = 0.42890447f * 0.37233013f;
        final float b = 0.2648958f * 0.05867535f;
        final float c = 0.8928169f * 0.7546882f;
        final float d = 0.0039135218f * 0.59395087f;
        final float e = 0.9114683f * 0.33522367f;

        System.out.println(a + b + c + d + e);
        System.out.println(a + b + c + e + d);
    }
}
Run Code Online (Sandbox Code Playgroud)