Зачем использовать Float.floatToIntBits() в сравнении с плавающей точкой в ​​Java?

В JBox2d для Vec2.equals() существует следующий код:

@Override
public boolean equals(Object obj) { //automatically generated by Eclipse
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Vec2 other = (Vec2) obj;
    if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
        return false;
    if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
        return false;
    return true;
}

Мне интересно, с какой целью здесь работают функции преобразования float ↔ int bit. Предоставляет ли это способ обойти проблему неточности сравнения с плавающей запятой Java (если это возможно)? Или это совсем другое? Мне интересно, является ли это альтернативой эпсилонному подходу:

if (Math.abs(floatVal1 - floatVal2) < epsilon)

PS. для полноты и интереса, здесь Vec2.hashCode():

@Override
public int hashCode() { //automatically generated by Eclipse
    final int prime = 31;
    int result = 1;
    result = prime * result + Float.floatToIntBits(x);
    result = prime * result + Float.floatToIntBits(y);
    return result;
}

FYI, я отлично вижу, почему функции преобразования используются в hashCode() - идентификаторы хэшей должны быть целыми числами.

Ответ 1

Объяснение можно найти в Joshua Bloch Эффективная Java: float и float требуется специальное лечение из-за существования -0.0, NaN, положительная бесконечность и отрицательная бесконечность. Вот почему Sun JVM Float.equals() выглядит так (6u21):

public boolean equals(Object obj)
{
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}

Итак, нет, Math.abs() с epsilon не является хорошей альтернативой. Из Javadoc:

Если f1 и f2 представляют Float.NaN, то метод equals возвращает true, хотя Float.NaN == Float.NaN имеет значение false. Если f1 представляет + 0.0f, тогда как f2 представляет -0.0f, или наоборот, равный тест имеет значение false, хотя 0.0f == - 0.0f имеет значение true.

Вот почему Eclipse автоматически генерирует код для вас.

Ответ 2

Double.Nan(Not-a-number) - это особое значение, когда дело доходит до сравнения:

System.out.println(Float.NaN == Float.NaN);
System.out.println(Float.floatToIntBits(Float.NaN) == Float.floatToIntBits(Float.NaN));

Отпечатки:

false
true 

Ответ 3

Я не знаю 100%, но, скорее всего, они пытаются обойти проблему NaN!= NaN. Если ваш float является NaN, вы не можете сравнивать ни с чем, так как результат всегда ложный. Сравнение intBits даст вам NaN == NaN.