Difficulty of compareTo on floats/doubles

John Rose john.r.rose at oracle.com
Tue Aug 5 05:07:59 UTC 2014


Read the proposal for the answer to that question, which is "wrong". 

– John

> On Aug 1, 2014, at 2:05 AM, Frank van Heeswijk <fvheeswijk at outlook.com> wrote:
> 
> Alex,
> 
> You gave the following class setup:
> 
> 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));
> 
> Correct me if wrong, but I was under the impression that value types could not hold object type references.
> My reasoning would be that the real values are stored directly on the stack, instead of a reference.
> 
> Could anyone confirm or deny this?
> 
> - Frank
> 
> > Date: Thu, 31 Jul 2014 15:25:30 -0700
> > Subject: Re: Difficulty of compareTo on floats/doubles
> > From: timeroot.alex at gmail.com
> > To: john.r.rose at oracle.com
> > CC: valhalla-dev at openjdk.java.net
> > 
> > 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