# Switching on float/double/long

Thu Dec 14 16:09:02 UTC 2017

```Switch on long: sure.

Switch on float/double: why?

As someone who puts nontrivial effort into trying to get developers in my
company to *stop* ever depending on exact equality of floats and doubles,
the only effect of this change will be to give me one additional thing to
tell people not to do.

We already have ==, !=, equals, assertEquals, and using as a key in that
list, so in a sense, "what's one more?". But -- were any actual advantages
to doing this mentioned in this thread? I don't see them. It seems like the
thread skipped right over that part?

If I had to guess, I'm guessing it might have something to do with the idea
that Float/Double will automatically get supported by pattern-matching and
there's nothing we can do about that. Is it something like that?

On Wed, Dec 13, 2017 at 5:44 PM, Paul Sandoz <paul.sandoz at oracle.com> wrote:

> Recently i was mildly annoyed to discover that Float/DoubleBuffer provide
> another variant of equality different to that of Float.equals/Arrays.equals
> and ==, specifically:
>
> *   This method considers two float elements {@code a} and {@code b}
> *   to be equal if
> *   {@code (a == b) || (Float.isNaN(a) && Float.isNaN(b))}.
> *   The values {@code -0.0} and {@code +0.0} are considered to be
> *   equal, unlike {@link Float#equals(Object)}.
>
> The vectorized implementations for float/double comparison/equality
> leverage the equivalent of floatToRawIntBits and on a mismatch have to
> check if it was caused by NaNs and if continue the search. The equivalent
> vectorized implementation for buffers (in progress) needs to do the same
> for NaNs and +0/-0.
>
> I can imagine bit-wise comparison is problematic since IIUC the actually
> bit pattern of a NaN value can, in a platform specific manner, change
> depending on how it’s operated on.
>
> Paul.
>
> > On 13 Dec 2017, at 16:51, John Rose <john.r.rose at oracle.com> wrote:
> >
> > Joe's points make perfect sense to me.
> >
> > Because of distinct problems with float, double, and reference operand
> > types, the "==" operator of Java is a poor equivalence relation, so just
> > referring the semantics of switch to op== is IMO a false start for
> defining
> > switch.  Switch-on-string has already broken with that false start, in
> the
> > case of references, using Object.equals.  A coherent way to break from
> > op== on floats is to, also, refer to the closest possible Object.equals
> > method, that on Float (and Double).  Joe's proposal in fact appeals to
> > the same standards, that of floatToIntBits.
> >
> > https://docs.oracle.com/javase/7/docs/api/java/lang/
> Float.html#equals(java.lang.Object)
> >
> > The most fine-grained equality relation that can be defined across all
> > types does not have an API point, but it can be called
> "substitutability".
> > For references substitutability is simply acmp, or op==(Object,Object).
> > For floats, substitutability is approximated by equality on
> floatToIntBits,
> > but defined rigorously by equality on floatToRawIntBits, which preserves
> > distinctions among NaNs.  Since those distinctions can be observed by
> > code, two distinct NaNs cannot be said to be substitutable for each
> > other.
> >
> > Joe's comparison, and that of Float.equals, is slightly more
> coarse-grained
> > of an equivalence relation, because all the NaNs are grouped into a
> single
> > cluster.  I wish the designer of Float.equals had not stopped
> arbitrarily at
> > NaN folding, and used floatToRawIntBits.  But, given that history, I
> think
> > when switch supports floats and doubles, it will use Joe's comparison.
> >
> > As Remi points out, suitable third-party extractors (or value type
> wrappers)
> > can provide other relations besides Joe's default, either distinguishing
> > NaNs or lumping zeroes.  Perhaps even rejecting NaNs, since they aren't
> > equal to themselves, supposedly.
> >
> > But we only get to set the default once.  So perhaps we should delay
> > supporting floats directly, until we can put all three or four float
> > matching predicates in front of us and decide which is the default.
> >
> > I see no corresponding reason to delay longs.  Instead, I see a pressing
> > need to figure out the correct relation between switch (x) { case
> (byte)1; }
> > where x might be a long or Long.  I don't see a way to delay that
> decision.
> >
> > Backing up a bit, I prefer to evaluate match semantics in terms of
> assignment
> > detection, rather than ad hoc equality predicates.  If the story is only
> > hoc, "if this type then this predicate" I am sure it will have more nasty
> > corners than it needs.  If it has an overarching principle, then I am
> sure
> > it will have nasty corners (as with +0 and NaNs), but only a minimum
> > of them.  And the overarching principle I prefer for match is to ask the
> > following polymorphic question:  "Could a value just like this case
> > expression have been assigned to that switch variable?"  This, IMO,
> > unwinds a lot of otherwise ad hoc special pleading.  It does require
> > some ad hoc definition of what "just like this" means, but the rest falls
> > out of prior JLS semantics.  Including the vexed questions which will
> > be occurring to you, above, about Long vs. long vs. byte.
> >
> > — John
> >
> > On Dec 12, 2017, at 1:52 PM, Remi Forax <forax at univ-mlv.fr> wrote:
> >>
> >> While we could do that, use bits representation for float and double,
> this is typically the kind of things that a user can also do with a record
> (a value type record ?) and a deconstructor, so in my opinion, we should
> not rush to implement this kind of switch given that we will soon provide a
> general mechanism to implement them outside of the JDK.
> >>
> >> Rémi
> >>
> >> De: "Brian Goetz" <brian.goetz at oracle.com>
> >> À: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> >> Envoyé: Lundi 11 Décembre 2017 22:25:34
> >> Objet: Switching on float/double/long
> >> A target of opportunity for the new switch JEP is to fill out the set
> of types that traditional switches can operate on -- specifically float,
> double, and long.  The reason that we don't support these now is mostly an
> accident of history; the `tableswitch` and `lookupswitch` opcodes are
> int-based, so the compiler doesn't have a convenient target for translating
> these.  As you've seen from the recent notes on switch translation, we're
> working towards using indy more broadly as a translation target for most
> switch constructs.  This makes it far easier to bust the limitations on
> switch argument types, and so this has been listed as a target of
> opportunity in the JEP (for both statement and expression switches.)
> >>
> >> Our resident floating-point expert, Joe Darcy, offers the following
> additional thoughts on the subject:
> >>
> >> -- Begin forwarded message
> >>
> >> Per a recent request from Brian, I've written a few thoughts about
> switching on floating-point values.
> >>
> >> To address some common misunderstandings of floating-point, while it is
> often recommended to *not* compare floating-point values for equality, it
> is perfectly well-defined to do such comparisons, it just might not do what
> you want
> >>
> >> For example, instead of
> >>
> >>     // Infinite loop since sum stored in d never exactly equals 1.0,
> doh!
> >>     while(d != 1.0)\u000B
> >>         d += 0.1;
> >>
> >> use either
> >>
> >>     // Counted loop
> >>     for(int i = 0; i < 10; i++)\u000B
> >>         d += 0.1;
> >>
> >> or
> >>
> >>     // Stop when numerical threshold is met
> >>     while(d <= 1.0)\u000B
> >>         d += 0.1;
> >>
> >> depending on the semantics the loop is trying to capture.
> >>
> >> I've attached a slide from my JVMLS talk this year to help illustrate
> the semantic modeling going in in IEEE 754 floating-point. Each of the 232
> possible bit patterns of a float is some floating-point value, likewise for
> the 264 possible bit patterns of a double. However, from a Java language or
> JVM perspective, there are not 232 or 264 distinct values we need or want
> to distinguish in most cases. In particular, we almost always want to treat
> all bit patterns which encode a NaN as a single conceptual NaN. Another
> wrinkle concerns zero: IEEE 754 has both a positive zero and a negative
> zero. Why are there *two* zeros? Because there are two infinities.  The
> signed infinities and distinguished by divide (1.0/0.0 => +infinity,
> 1.0/-0.0 => -infinity) and by various library functions.
> >>
> >> So we want to:
> >>
> >>     * Allow every distinct finite nonzero floating-point value to be
> the case of a switch.
> >>     * Allow -0.0 and +0.0 to be treated separately.
> >>     * Allow -infinity and +infinity to be treated separately.
> >>     * Collapse all NaN representation as a single value.
> >>
> >> For the "Rounding" mapping in the diagram which goes from the extended
> real numbers to floating-point data, there is a nonempty segment of the
> real number line which maps to a given representable floating-point number.
> For example, besides the string "1.0" mapping exactly to the reprentable
> floating-point value 1.0, there is a region slightly small than 1
> (0.99999999999999999999...) which will round up to 1.0 and a region
> slightly larger than 1 (1.000000000000000001...) which will round down to 1
> from decimal -> binary conversion. This would need to be factored into any
> distinctiveness requirements for the different arms of the switch. In other
> words
> >>
> >>     case 1.000000000000000001:
> >>     ....
> >>     case 0.99999999999999999999
> >>     ...
> >>
> >> would need to be rejected just as
> >>
> >>     case 0:
> >>     ....
> >>     case 00:
> >>
> >> is rejected.
> >>
> >> In terms of JDK 9 structures and operations, the following
> transformation of a float switch has what I think are reasonable semantics:
> >>
> >>     Replace each float case label y in the source with an int label
> resulting from floatToIntBits(y). Note that floatToIntBits is used for the
> mapping rather than floatToRawIntBits since we want NaNs to be grouped
> together.
> >>
> >>     Instead of switching on float value x, switch on floatToIntBits(x).
> >>
> >> HTH,
> >>
> >> -Joe
> >
>
>

--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
```