1

I have some items which have an id and a value and I am looking for the maximum element.

The values are floats/doubles and as tie breaking I want to use the object with the smaller id.

One approach is the following:

double maxValue = Double.NEGATIVE_INFINITY;
Item maxItem = null;
for (Item item : items) {
    if (item.value() > maxValue) {
        maxValue = item.value();
        maxItem = item;
    } else if (item.value() == maxValue && item.id() < maxItem.id()) {
        maxItem = item;
    }
}

However, this includes an quality-comparison using floating point numbers, which is discouraged and in my case also creates a critical issue in the code analysis step.

Of course, I can write something to avoid the issue, e.g. use >= for the second comparison, however from the point of readability my future me or any other reader might wonder if it is a bug.

My question: Is there an approach which expresses well the intent and also avoids float comparison using == for this task?

kap
  • 1,456
  • 2
  • 19
  • 24
  • You could just add comments to your code to explain it to your future self or someone else who will be looking at your code in the future. – Jesper Feb 22 '16 at 14:37
  • 4
    It's already obvious that this is not one the situations that the "float equality FUD" warns about. – harold Feb 22 '16 at 14:40
  • What exactly is the question? Are you looking for something that says "okay those two floats are not truely equal but they're close enough"? – Tunaki Feb 22 '16 at 15:45
  • See [this](http://stackoverflow.com/questions/1088216/whats-wrong-with-using-to-compare-floats-in-java). What is the "*critical issue*"? – user1803551 Feb 22 '16 at 15:46

2 Answers2

0

In this case there is nothing wrong with testing equality of two floating point values. Two float/double values can be equal, and this can be tested with the == operator.

One reason that using == is discouraged for floating point values is that comparing them with literal constants can lead to unexpected behavior because the literals are usually written in decimal notation, whereas the floating point variables are stored in binary. Not all decimal values can be exactly represented in binary, and therefore the value of the variables are only approximations of the decimal values. For example: 3.0 * 0.1 == 0.3 evaluates to false.

Another reason is that floating point values do not always behave like real numbers. In particular, floating point operations are not necessarily commutative (x * y == y * x) and associative ((x * y) * z == x * (y * z)). For example, (0.3 * 0.2) * 0.1 == 0.3 * (0.2 * 0.1) evaluates to false.

In your case, however, there is no reason not to use ==.

Hoopje
  • 12,677
  • 8
  • 34
  • 50
  • Not disagreeing, but there is an other reason too: even without literals, two computations that would be expected to give equal results in ℝ are often different in floats. This reason is also interesting, because without it we could just decide to not write misleading literals and solve the problem, but that's not enough. – harold Feb 22 '16 at 14:57
  • @harold Thanks, I incorporated that into the answer. – Hoopje Feb 22 '16 at 15:18
0

You could write a Comparator<Item> and then use it to find the greatest item:

Comparator<Item> byValueAscThenIdDesc = (i1, i2) -> {
    int valueComparison = Double.compare(i1.value(), i2.value());
    if(valueComparison == 0) {
        int idComprison = Integer.compare(i1.id(), i2.id());
        return -idComparison;
    }
    return valueComparison;
};

List<Item> items = new ArrayList<>();

Item max = items.stream().max(byValueAscThenIdDesc).get();
Johnbot
  • 2,169
  • 1
  • 24
  • 24