简体   繁体   English

浮点数的Java哈希码

[英]java hashcode for floating point numbers

I want to use Double (or Float ) as keys in a Hashmap 我想使用Double (或Float )作为Hashmap

Map<Double, String> map = new HashMap<Double, String>()
map.put(1.0, "one");
System.out.println(map.containsKey(Math.tan(Math.PI / 4)));

and this returns false. 这将返回false。

if I were comparing these two numbers I would have done something like this 如果我比较这两个数字,我会做这样的事情

final double EPSILON = 1e-6;
Math.abs(1.0 - Math.tan(Math.PI / 4)) < EPSILON

But since Hashmap would use hashcode it breaks things for me. 但是由于Hashmap将使用hashcode因此对我来说很Hashmap

I thought to implement a roundKey function that rounds to some multiple of EPSILON before using it as a key 我想实现一个roundKey函数,在将它用作键之前将其四舍五入到EPSILON倍数

map.put(roundKey(1.0), "one")
map.containsKey(roundKey(Math.tan(Math.PI / 4)))
  • is there a better way ? 有没有更好的办法 ?
  • what is the right way to implement this roundKey 什么是实现此roundKey的正确方法

If you know what rounding is appropriate, you can use that. 如果您知道合适的舍入方法,则可以使用它。 eg if you need to round to cents, you can round to two decimal places. 例如,如果需要四舍五入到美分,则可以四舍五入到小数点后两位。

However, for the example above discrete rounding to a fixed precision might not be appropriate. 但是,对于上面的示例,将离散舍入到固定精度可能不合适。 eg if you round to 6 decimal places, 1.4999e-6 and 1.5001e-6 will not match as one rounds up and the other down even though the difference is << 1e-6. 例如,如果四舍五入到小数点后六位,则即使相差<< << 1e-6,1.4999e-6和1.5001e-6也不会匹配,因为一个向上舍入而另一个向下舍入。

In that situation the closest you can do is to use a NavigableMap 在这种情况下,您最能做的就是使用NavigableMap

NavigableMap<Double, String> map = new TreeMap<>();

double x = ....;
double error = 1e-6;

NavigableMap<Double, String> map2 = map.subMap(x - error, x + error);

or you can use 或者你可以使用

Map.Entry<Double, String> higher = map.higherEntry(x);
Map.Entry<Double, String> lower = map.lowerEntry(x);
Map.Entry<Double, String> entry = null;
if (higher == null)
    entry = lower;
else if (lower == null)
    entry = higher;
else if (Math.abs(lower.getKey() - x) < Math.abs(higher.getkey() - x))
    entry = lower;
else
    entry = higher;
// entry is the closest match.
if (entry != null && Math.abs(entry - x) < error) {
    // found the closest entry within the error
}

This will find all the entries within a continuous range. 这将找到连续范围内的所有条目。

Best way is to not use floating point numbers as keys, as they are (as you discovered) not going to compare. 最好的方法是不要使用浮点数作为键,因为它们(如您所发现的)不会进行比较。
Kludgy "solutions" like calling them identical if they're within a certain range of each other only lead to problems later, as you're either going to have to stretch the filter or make it more strict in time, both leading to potential problems with existing code, and/or people will forget how things were supposed to work. Kludgy的“解决方案”就像在它们彼此处于一定范围之内时称它们相同,只会在以后导致问题,因为您要么不得不拉伸过滤器,要么使过滤器的时间变得更加严格,两者都会导致潜在的问题与现有的代码,和/或人们会忘记事情应该如何工作。
Of course in some applications you want to do that, but as a key for looking up something? 当然,在某些应用程序中您想这样做,但是作为查找内容的关键吗? No. You're probably better off using angles in degrees, and as integers, as the keys here. 不。您最好使用角度(以度为单位)和整数(如此处的键)。 If you need greater precision than 1 degree, use the angle in eg tenth of degrees by storing a number of 0 through 3600. 如果需要大于1度的精度,请通过存储0到3600之间的数字来使用角度(例如十分之一度)。
That will give you reliable behaviour of your Map while retaining the data you're planning to store. 这将为您提供可靠的Map行为,同时保留您计划存储的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM