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

- liangchenblue at gmail.com
Mon Mar 11 16:19:15 UTC 2024


Hi Remi,
I believe we can stick with bitwise equivalence, at least for now. The
bitwise equivalence, in essence, is a remedy for the compatibility issues
around == with the removal of identity. Thus, it should not replace and is
still not preferable to equals(), even though the migration happens to make
== a good implementation in many more cases.

For a Double object d, there are sets S0 that are all objects with the same
identity as d, S1 that are all objects with the same bitwise representation
(headers ignored of course) as d, S2 that are all objects with the same
representational equivalence as d, and Sequals that are all objects that
equals(d). We can see (<= for "is subset of") {d} == S0 <= S1 <= S2 <=
(actually ==) Sequals, and whatever the set d == holds true against (call
it S==)  has S== <= Sequals.

With the removal of identity, S0 is gone, so S1 becomes S==. What you call
for is to use S2 for S==. I believe that the move from S1 to S2 will be a
backward-compatible change in the future, but not from S2 to S1. Given the
significant performance benefits of using S1 for S== instead of S2, I
believe we can stay with the bitwise equivalence and investigate using S2
for S== if the future hardware improvements make it feasible.

Regards,
Chen Liang

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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20240311/3ee081fd/attachment.htm>


More information about the valhalla-spec-observers mailing list