Float to byte and back in switch

Angelos Bimpoudis angelos.bimpoudis at oracle.com
Thu Oct 19 23:41:31 UTC 2023


That section defines the runtime semantics of switch label selection indeed.

What is also important is the kind of types that can be "allowed" to reach this point. The permitted case constant associations are shown in 14.11.1 Switch Blocks where we split (add) the new kinds of constants separately.
if T is one of char, byte, short, int, Character, Byte, Short, Integer, or String, the constant is assignment compatible with T.

if T is one of long, float, double, boolean, Long, Float, Double, Boolean, the constant is the same type:

with T if T is a primitive type, or
with T' where T' is T after unboxing conversion, otherwise.
That second bullet essentially says that a selector of a float type can match only float constant labels and so on and so forth.

While comparing y to 1 (the integer one) is crystal clear, there are other occasions that this is not. Consider this:

jshell> float f = 16777216 // 224 and that can be represented exactly into a float
f ==> 1.6777216E7

jshell> f == 16777217 // 224+1 that cannot be represented exactly because the bits of the mantissa do not suffice, so the RHS constant maps also to 1.6777216E7
$7 ==> true

As result the following switch is not permitted:

    void test(float f) {
        switch (f) {
            case 16777216:
                break;
            case 16777217:
                break;
            default:
                break;
        }
    }

both labels map to the same float while the two labels are clearly, representationally different (among them). That would lead to great confusion.
________________________________
From: amber-dev <amber-dev-retn at openjdk.org> on behalf of Ella Ananeva <ella.ananeva at oracle.com>
Sent: 19 October 2023 19:01
To: amber-dev at openjdk.org <amber-dev at openjdk.org>
Subject: Float to byte and back in switch


Hi team,

When we match labels in switch with primitive types, the spec defines the behavior for integral types and for floating-point types.

•  If T is a primitive type, then a  case label with a case constant c applies to a value that is of type char, byte, short, int,  long, float, double, boolean, String or an enum type if the constant c is equal to the value.

Equality is defined in terms of the == operator (15.21<https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.21>) for the integral types and the boolean type, and in terms of representation equivalence (java.lang.Double<https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Double.html#repEquivalence>) for the floating-point types.  If the value is a String,  equality is defined in terms of the equals method of class String.



What should be the behavior, though, when we try to match an integral type to a floating-point type?

e.g., In this scenario:

private static void testSwitch(float y) {
    System.out.println(y == 1);     //prints true
    switch (y) {
        case 1 -> { System.out.println(y == 1);}//throws a compile-time error: constant label of type int is not compatible with switch selector type float

        default -> { }
    }
}



I would expect the compiler to be able to handle this situation: since int can be converted to float and the equality operator returns true, the label should be chosen.



The opposite situation with narrowing conversion, when we try to convert float to int may be trickier, but shouldn’t it also be accepted?



Thank you,

Ella Ananeva




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20231019/9f612803/attachment-0001.htm>


More information about the amber-dev mailing list