Difficulty of compareTo on floats/doubles

Alex Meiburg timeroot.alex at gmail.com
Thu Jul 31 22:25:30 UTC 2014


So the current state is that for...
primitives:
    == and != performs bitwise comparison
    equals() doesn't exist
Objects:
    == and != perform identity testing
    equals() is a method that can be overriden

With regards to equality on value types, it sounds like the main proposals
are...
values, option 1:
    == performs elementwise testing, via "vcmp"
    equals() defaults to vcmp, but can be overridden

values, option 2:
    == and equals() perform elementwise testing, via "vcmp". equals() is
final.

values, option 3:
    == doesn't exist on the unboxed values.
    equals() defaults to vcmp, but can be overridden

And then on polymorphic generics that could be primitive, value, or Object,
there are
generics, option 1:
    == will perform just as == would on the corresponding
Object/primitive/value
    equals() will call the method on the Object/value, and
Float.equals()/Integer.equals()/etc. on primitives

generics, option 2:
    == is an identity check on Objects, and equals() on primitive/value
    equals() will call the method on the Object/value, and
Float.equals()/Integer.equals()/etc. on primitives

generics, option 3:
    == doesn't exist on polymorphic variables
    equals() will call the method on the Object/value, and
Float.equals()/Integer.equals()/etc. on primitives

and the situation you're describing is option 1 for values, and option 3
for generics. (The numbering is arbitrary, of course).
Would allowing equals to be overridden be worth it, on value types?
Although certainly mostly a lack of imagination, I feel like the use cases
to redefine it would be limited -- and there is one other thing to
consider, of how the equality check should recurse onto objects members of
the value type. For instance, given a situation like

class Bar {
   int f;
   public Bar(int f){ this.f = f; }
   public boolean equals(Object that){ return (that instanceof Bar) &&
((Bar)that).f == f; }
}

final __ByValue class Foo {
    int x;
    Bar y;
}


Foo a = __MakeValue(5, new Bar(10));
Foo b = __MakeValue(5, new Bar(10));

System.out.println(a == b);
System.our.println(a.equals(b));

Then my first expectation would be for them to print "false" and "true",
respectively. That is, if "==" were a vcmp, it would check x's for integer
bitwise equality, and the reference y the same way. The "equals" method I
would intuitively expect to do similar bitwise comparison on x, but a call
to y.equals(). This follows along with the general thought process that
when I'm treating it like an int I want a quick and "dumb" equality check,
whereas the more class-seeming equals() call is something I can expect to
be a "deep" check. I may be alone in expecting this. :) But if others agree
that it would be a logical behavior, then I would support defining == to be
recursive == on reference fields, and equals() to be a (probably final)
method doing the above.

-- Alexander Meiburg


2014-07-31 13:56 GMT-07:00 John Rose <john.r.rose at oracle.com>:

> On Jul 31, 2014, at 12:43 PM, Alex M <timeroot.alex at gmail.com> wrote:
>
> > strange problems
>
> The essential point here is discussed in passing (see "simple
> relationals") in the value type prospectus.
>   http://cr.openjdk.java.net/~jrose/values/values.html
>
> Yes, there are a lot of these strange problems that arise from forcing
> both primitives and objects under one type bound.
>
> More generally, the semantic oddities of "==" for float, double, and
> references make it very tricky indeed to apply "==" (and other operators,
> notably "+") to extremely polymorphic variables bounded by "any".
>
> Although it may not be practical in the end, my personal preference would
> be to deprecate or disallow operators on polymorphic variables, and express
> everything with method invocation.
>
> Method invocation on a non-reference value can be uniformly and simply
> defined by delegation to a boxed version of the value. If we define new box
> types (not impossible though difficult) we can take extra care to have the
> ad hoc polymorphism be as consistent as possible across the expanded range
> of types.  We provide for such consistency already in the documentation of
> interfaces like Comparable and (as Joe explained) methods like
> Double.compareTo.
>
> Pre-existing boxes are probably not adequate to this.  Null references may
> also require a "boxing" rule of some sort.
>
> See also the "vcmp" instruction in the value types prospectus.  For value
> types we think we can make a compatible story of how to cope with "==" and
> ".equals":  "==" is bitwise and ".equals" is a method call (possibly boxed,
> but the user cannot observe whether that happens).
>
> Bottom lines:  Consistent ad hoc polymorphism is hard, especially when
> unifying legacy types.  And see "vcmp" for bitwise semantics and extend it
> if necessary to primitives.  But try hard to do most things in terms of
> methods, which is more flexible and explicit.
>
> — John


More information about the valhalla-dev mailing list