Feature suggestion: Add static equals methods to Float and Double

John Rose john.r.rose at oracle.com
Tue Jan 8 01:34:32 UTC 2019

As I think you expect, isSubstitutible(x,y) will mean that x and y are equivalent
for all practical purposes.  One hard question is nailing down what are
"all practical purposes".  Certainly it's unfair to flip a coin while evaluating
x and y separately, and claim that a distinct outcome proves a difference.
What about viewing the bits of x and y using the Unsafe API?  That's
unfair also, since it opens the door to implementation-dependent behavior
which might detect a difference (an irrelevant difference) between x and y.

Now, floatToRawIntBits can detect differences between NaNs which
have different numeric codes.  Are two such NaNs substitutable or not?
The evidence in favor:
 - They become equivalent when boxed in a Float, and Float claims to
  be an all-purpose box for float values.
 - The extra information produced by floatToRawIntBits is implementation
  specific, and in particular processor dependent.
 - Joe Darcy suggested to me that some processors, like x87, may
  perturb NaN bits (turning off the "signalling" bit, for example), even
  if the float value is simply bound to a parameter.  This means that
  the operand to floatToRawIntBits *might*, in compiled code, possibly
  have *some* of its bits perturbed.  (Thanks, Joe, for that and similar
  hair-raising stories.)
 - The previous point implies that compiled code and interpreted code
  might, in the same JVM instance, produce different results on the
  same argument.  That is quite implementation specific indeed!

The evidence against:
 - The existing standard API point floatToRawIntBits is not going away.
  So the isSubstitutable API point must document that floatToRawIntBits
  has the processor-dependent ability to conjure up different bits for
  x and y.  Maybe it should be called isAlmostSubstitutable??

The right trade-off here, I think, is to align isSubstitutable with Float::equals
and simply increase the warnings on floatToRawIntBits, that this method
can produce platform-specific results in an unpredictable way, and that
in particular it can produce distinct answers for otherwise substitutable

I also suggested to Dr. Deprecator (Stuart Marks) that floatToRawIntBits
might be a candidate for deprecation; he said it would be a lot of expense
for relatively little benefit.  I think at least the javadoc for floatToRawIntBits
should not speak so confidently, as it does, of "preserving Not-a-Number
(NaN) values", as if these values were something that had a stable
semantics, as if they could somehow carry application information.

More background (thanks again to Joe):  The NaN bits don't have a standard
format.  Different CPUs can (and often do) disagree on which bits
mean what, and how standard arithmetic operations consume and produce
them.  There is apparently no agreed standard NaN pattern, although
Java favors the "all zero" bit pattern as normative.  Different CPUs
may disagree on which bits denote signaling or quiet NaNs, and when
such bits may be queried or modified.  Adding the possible distinct
treatment of the "same" NaN value in compiled vs. interpreted code
(as well as strictfp vs. non-strictfp code), and the use of "raw" NaN
bits seems a very risky proposition, useful only for people writing
processor-specific code, with great care.

In hindsight, I think it would have been nice to place the "raw bits"
API points to Unsafe or a separate module.  But when those API
points were designed (1.0), there were no such hiding places.
And it's probably too costly to fix now.  If the sweet spot is to
acknowledge the wart, but not let it spread, then we design the
substitutability test based on Float::equals, not floatToRawIntBits.

— John

On Jan 6, 2019, at 4:36 PM, Hans Boehm <hboehm at google.com> wrote:
> IIUC,  isSubstitutible() is not quite what's being proposed here. The
> proposed definition here uses floatToIntBits(), not floatToRawIntBits().
> Hans
> On Sun, Jan 6, 2019 at 3:59 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>> Followers of Project Valhalla will see that this issue comes up when
>> defining equality on value types.  The relation you are looking for is
>> being called "substitutible"; it asks whether there is any way to
>> distinguish between two values.  For primitives other than float/double,
>> this coincides with `==`, and similarly for references.
>> An `isSubstitutible()` API point will likely emerge from Valhalla.

More information about the core-libs-dev mailing list