<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body style="overflow-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;">
<div>
<blockquote type="cite">
<div>On Feb 9, 2024, at 10:13 AM, Remi Forax <forax@univ-mlv.fr> wrote:</div>
<div><br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
</div>
</blockquote>
<blockquote type="cite">
<blockquote type="cite">value class C {<br>
private double d;<br>
C(double d) { this.d = d; }<br>
long bits() { return Double.doubleToRawLongBits(d); }<br>
}</blockquote>
</blockquote>
<blockquote type="cite">
<blockquote type="cite">C c1 = new C(Double.longBitsToDouble(0x7ff0000000000001L));<br>
C c2 = new C(Double.longBitsToDouble(0x7ff0000000000002L));<br>
assert c1.bits() != c2.bits();<br>
<br>
Will this assert ever fail? Well, it depends on the JVM treats c1 and c2 as belonging to the same equivalence class. If they are, it's allowed to substitute c1 for c2 at any time. I think it's pretty clear that would be a mistake.</blockquote>
</blockquote>
<blockquote type="cite">
<div><br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
<span style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;">I
do not compute that statement :)</span><br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
<br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
<span style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;">Why
do you want users to care about the bitwise representation of NaN ?</span><br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
<span style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;">Both
0x7ff0000000000001L and 0x7ff0000000000002L represents NaN, if we print c1.d and c2.d both will print NaN, if we use c1.d or c2.d in numeric computation, they will both behave as NaN.</span><br style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none;">
</div>
</blockquote>
<div><br>
</div>
<div>To be very specific about this example, I think it's bad if the result of the 'c.bits()' method is nondeterministic, making the 'assert' result unpredictable.</div>
<div><br>
</div>
<div>Two instances of C with representationally-equivalent state can produce different results from their 'c.bits()' method. So they aren't substitutable (per the "can substituted for one another without changing the result of the expression" definition). I
would be uncomfortable with the JVM substituting one for the other whenever it wants to.</div>
<div><br>
</div>
<div>Sure, the *native* operations on *double* almost never distinguish between different NaN encodings. But a *custom* operation on *a class that wraps a double* certainly can.</div>
<div><br>
</div>
<div>(The example could be improved by doing a better job of illustrating that the double is private internal state, and that the operations exposed by the class need not look at all like floating-point operations, as far as the client of the class is concerned.
All they know is they've got an object that is randomly producing nondeterministic results.)</div>
<div><br>
</div>
<div>This, by itself, is not an argument for '==' being defined to use bitwise equivalence, but it is an argument for a well-defined concept of "substitutable value object" that is based on bitwise equivalence.</div>
<div><br>
</div>
<div>
<blockquote type="cite">Using your example, but with a value record (supposing the bitwise equivalence)<br>
<br>
value record C(double d) { }<br>
C c1 = new C(Double.longBitsToDouble(0x7ff0000000000001L));<br>
C c2 = new C(Double.longBitsToDouble(0x7ff0000000000002L));<br>
<br>
System.out.println(c1); // C[d=NaN]<br>
System.out.println(c2); // C[d=NaN]<br>
System.out.println(c1 == c2); // false ??<br>
System.out.println(c1.equals(c2)); // true<br>
</blockquote>
<br>
</div>
<div>Sure. I mean, this is the exact same result as you get with an identity record, so I don't think it should be surprising. The fallacy, I think, is in expecting '==' for value objects to be something more than a substitutability test. (Which, like I said,
could be done, but it seems like a distraction when what you really want to use is 'equals'.)</div>
<div><br>
</div>
<div>Compare:</div>
<div><br>
</div>
<div>value record S(String s) {}</div>
<div>S s1 = new S("abc");</div>
<div>S s2 = new S("abcd".substring(0,3));</div>
<div><br>
</div>
<div>System.out.println(s1); // S[s=abc]</div>
<div>System.out.println(s2); // S[s=abc]</div>
<div>System.out.println(s1 == s2); // false</div>
<div>System.out.println(s1.quals(s2)); // true</div>
<div><br>
</div>
<div>This may be a new concept to learn: value objects with double fields can be 'equals' but not '==', just like value objects with reference fields can be 'equals' but not '=='. But I think that's a better quirk to learn than '==' sometimes not meaning "substitutable".</div>
<div><br>
</div>
</div>
</body>
</html>