raw floating-point bits in '==' value object comparisons (again/still)

Clement Cherlin ccherlin at gmail.com
Mon Mar 11 15:27:30 UTC 2024


On Mon, Mar 11, 2024 at 8:34 AM Remi Forax <forax at univ-mlv.fr> wrote:
>
> Last week, I explain at JChateau (think JCrete in France, less sun, more chateaux) how value types work from the user POV, among other subject describing the semantics of ==.
>
> First, most of the attendee knew the semantics difference between == on double and Double.equals(). I suppose it's because people that attend to such (un-)conference have a more intimate knowledge of Java than an average developer. Second, no attendee knew that NaN was a prefix.
>
> So it let me think again on that subject.
>
> 1) The argument that of Dan that we want to be able to create a class with two different NaN, does not hold because instead of storing the values as double, the values can be stored as long.
>
>   value class C {
>       private double d;
>       C(double d) { this.d = d; }
>       long bits() { return Double.doubleToRawLongBits(d); }
>   }
>
>   C c1 = new C(Double.longBitsToDouble(0x7ff0000000000001L));
>   C c2 = new C(Double.longBitsToDouble(0x7ff0000000000002L));
>   assert c1.bits() != c2.bits();
>
> can be rewritten as
>
>   value class C {
>       private long l;
>       C(double d) { this.l = Double.doubleToRawLongBits(d); }
>       long bits() { return l; }
>   }
>
>
> 2) The de-duplication of value instances by the GC works with both the bitwise equivalence and the representational equivalence.
>
>  If the GC only de-duplicate the value instance based only on the bitwise equivalence, it is a valid algorithm under the representational equivalence.
>
>
> So I not convinced that the bitwise equivalence should be choosen instead of the representational equivalence, for me two semantics instead of three is a win.
>
> Rémi

Let's make nobody happy and define new operators for new semantics, à
la JavaScript:

Given double d1, d2;
d1 === d2 uses the algorithm implemented in Double.equals()
d1 ==== d2 uses the algorithm implemented in Double.equals() but with
doubleToRawLongBits instead of doubleToLongBits.

Same goes for floats. For float ===/==== double, the float is coerced
to double first, just as with == today.

These operators behave exactly the same as == for reference types
(except primitive wrappers, which they unbox first if both are
non-null) and non-floating-point primitive types, and apply their
respective definitions of equality recursively to the fields of value
types.

Alternatively, === is shorthand for Objects.equals() and we can all
stop using == for 99% of purposes (again, like JavaScript).

Cheers,
Clement Cherlin


More information about the valhalla-spec-observers mailing list