Реализуйте "допускающий" `equals` &` hashCode` для класса с членами с плавающей запятой - PullRequest
10 голосов
/ 08 декабря 2010

У меня есть класс с полем float.Например:

public class MultipleFields {
  final int   count;
  final float floatValue;

  public MultipleFields(int count, float floatValue) {
    this.count = count;
    this.floatValue = floatValue;
  }

}

Мне нужно иметь возможность сравнивать экземпляры по значению.Теперь, как правильно реализовать equals & hashCode?

Обычный способ реализации equals и hashCode - просто рассмотреть все поля.Например, Eclipse сгенерирует следующее equals:

  public boolean equals(Object obj) {
    // irrelevant type checks removed
    ....
    MultipleFields other = (MultipleFields) obj;
    if (count != other.count)
      return false;
    if (Float.floatToIntBits(floatValue) != Float.floatToIntBits(other.floatValue))
      return false;
    return true;
  }

(и аналогичный hashCode, который по существу вычисляет count* 31 + Float.floatToIntBits(floatValue)).

Проблема в том, что мои значения FPподвержены ошибкам округления (они могут исходить из пользовательского ввода, из БД и т. д.).Поэтому мне нужно «толерантное» сравнение.

Распространенным решением является сравнение с использованием значения эпсилона (см., Например, Сравнение IEEE-чисел с плавающей запятой и двойных для равенства ).Тем не менее, я не совсем уверен, как я могу реализовать equals, используя этот метод, и все еще есть hashCode, который соответствует equals.

Моя идея состоит в том, чтобы определить количество значащих цифр для сравнения, затем всегда округлять до этого количества цифр как equals, так и hashCode:

long comparisonFloatValue = Math.round(floatValue* (Math.pow(10, RELEVANT_DIGITS)));

Тогда, если я заменюпри всех применениях floatValue с comparisonFloatValue в equals и hashCode я должен получить «толерантное» сравнение, которое соответствует hashCode.

  • Будет ли это работать?
  • Видите ли вы какие-либо проблемы с этим подходом?
  • Есть ли лучший способ сделать это?Это кажется довольно сложным.

1 Ответ

11 голосов
/ 08 декабря 2010

Большая проблема с этим состоит в том, что два значения с плавающей точкой могут все еще быть очень близко друг к другу , но все равно сравниваться неравно. По сути, вы делите диапазон значений с плавающей запятой на сегменты - и два значения могут быть очень близко друг к другу, не находясь в одном сегменте. Представьте, что вы использовали две значащие цифры, применяя усечение для получения сегмента, например ... тогда 11.999999 и 12.000001 были бы неравны, но 12.000001 и 12.9999999 были бы равны, несмотря на то, что они намного дальше друг от друга.

К сожалению, если у вас нет таких значений, как это, вы не можете реализовать равные соответствующим образом из-за транзитивности: x и y могут быть близко друг к другу, y и z могут быть близко друг к другу, но это не означает, что x и z находятся близко друг к другу.

...