RFR: 8341260: Add Float16 to jdk.incubator.vector

Joe Darcy darcy at openjdk.org
Sat Oct 19 21:02:54 UTC 2024


On Thu, 17 Oct 2024 23:37:54 GMT, Joe Darcy <darcy at openjdk.org> wrote:

> ```
> $ diff test/jdk/java/lang/Float16/BasicFloat16ArithTests.java  ~/jdk24/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java 
> 26c26,27
> <  * @bug 8329817 8334432 8339076
> ---
> >  * @bug 8329817 8334432 8339076 8341260
> >  * @modules jdk.incubator.vector
> 30c31,32
> < import static java.lang.Float16.*;
> ---
> > import jdk.incubator.vector.Float16;
> > import static jdk.incubator.vector.Float16.*;
> 
> $ diff test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java  ~/jdk24/test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java 
> 26c26
> <  * @bug 8205592 8339252
> ---
> >  * @bug 8205592 8339252 8341260
> 27a28
> >  * @modules jdk.incubator.vector
> 37a39
> > import jdk.incubator.vector.Float16;
> 110c112
> <         Float16 res = bv.float16Value();
> ---
> >         Float16 res =  Float16.valueOf(bv); // bv.float16Value();
> ```

Initially, I left the tests for the BigDecimal -> Float16 conversion in the tests for the base module to ease review compared to their location in the Valhalla branch. The tests can be extracted and moved to a jdk.incubator.vector area subsequently.

> To ease review, diffs of corresponding files from the current, at time of writing, state of files in lworld+fp16 vs a port to jdk.incubator.vector, starting with Float16:
> 
> ```
> $ diff src/java.base/share/classes/java/lang/Float16.java  ~/jdk24/open/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16.java 
> 26c26
> < package java.lang;
> ---
> > package jdk.incubator.vector;
> 28a29
> > import java.math.BigInteger;
> 30,31c31,32
> < import jdk.internal.math.*;
> < import jdk.internal.vm.annotation.IntrinsicCandidate;
> ---
> > // import jdk.internal.math.*;
> > // import jdk.internal.vm.annotation.IntrinsicCandidate;
> 37c38
> <  * The {@code Float16} is a primitive value class holding 16-bit data
> ---
> >  * The {@code Float16} is a class holding 16-bit data
> 46,48d46
> <  * <p>This is a <a href="https://openjdk.org/jeps/401">primitive value class</a> and its objects are
> <  * identity-less non-nullable value objects.
> <  *
> 52a51,56
> >  * <p>This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
> >  * class; programmers should treat instances that are
> >  * {@linkplain #equals(Object) equal} as interchangeable and should not
> >  * use instances for synchronization, or unpredictable behavior may
> >  * occur. For example, in a future release, synchronization may fail.
> >  *
> 62,64d65
> <  *
> <  * @author Jatin Bhateja
> <  * @since 20.00
> 69,70c70,71
> < @jdk.internal.MigratedValueClass
> < @jdk.internal.ValueBased
> ---
> > // @jdk.internal.MigratedValueClass
> > //@jdk.internal.ValueBased
> 213c214,215
> <         return FloatToDecimal.toString(f16.floatValue());
> ---
> >         return Float.toString(f16.floatValue());
> >         // return FloatToDecimal.toString(f16.floatValue());
> 420d421
> <      * @see BigDecimal#float16Value()
> 423c424,539
> <         return v.float16Value();
> ---
> >         return BigDecimalConversion.float16Value(v);
> >     }
> > 
> >     private class BigDecimalConversion {
> >         /*
> >          * Let l = log_2(10).
> >          * Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l).
> >          */
> >         private static final double L = 3.321928094887362;
> > 
> >         private static final int P_F16 = PRECISION;  // 11
> >         private static final int Q_MIN_F16 = MIN_EXPONENT - (P_F16 - 1);  // -24
> >         private static final int Q_MAX_F16 = MAX_EXPONENT - (P_F16 - 1);  // 5
> > 
> >         /**
> >          * Powers of 10 which can be represented exactly in {@code
> >          * Float16}.
> >          */
> >         private static final Float16[] FLOAT16_10_POW = {
> >             Float16.valueOf(1), Float16.valueOf(10), Float16.valueOf(100),
> >             Float16.valueOf(1_000), Float16.valueOf(10_000)
> >         };
> > 
> >         public static Float16 float16Value(BigDecimal bd) {
> > //             int scale = bd.scale();
> > //             BigInteger unscaledValue = bd.unscaledValue();
> > 
> > //              if (unscaledValue.abs().compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
> > //                 long intCompact = bd.longValue();
> > //                 Float16 v = Float16.valueOf(intCompact);
> > //                 if (scale == 0) {
> > //                     return v;
> > //                 }
> > //                 /*
> > //                  * The discussion for the double case also applies here. That is,
> > //                  * the following test is precise for all long values, but here
> > //                  * Long.MAX_VALUE is not an issue.
> > //                  */
> > //                 if (v.longValue() == intCompact) {
> > //                     if (0 < scale && scale < FLOAT16_10_POW.length) {
> > //                         return Float16.divide(v, FLOAT16_10_POW[scale]);
> > //                     }
> > //                     if (0 > scale && scale > -FLOAT16_10_POW.length) {
> > //                         return Float16.multiply(v, FLOAT16_10_POW[-scale]);
> > //                     }
> > //                 }
> > //             }
> >             return fullFloat16Value(bd);
> >         }
> > 
> >         private static BigInteger bigTenToThe(int scale) {
> >             return BigInteger.TEN.pow(scale);
> >         }
> > 
> >         private static Float16 fullFloat16Value(BigDecimal bd) {
> >             if (BigDecimal.ZERO.compareTo(bd) == 0) {
> >                 return Float16.valueOf(0);
> >             }
> >             BigInteger w = bd.unscaledValue().abs();
> >             int scale = bd.scale();
> >             long qb = w.bitLength() - (long) Math.ceil(scale * L);
> >             Float16 signum = Float16.valueOf(bd.signum());
> >             if (qb < Q_MIN_F16 - 2) {  // qb < -26
> >                 return Float16.multiply(signum, Float16.valueOf(0));
> >             }
> >             if (qb > Q_MAX_F16 + P_F16 + 1) {  // qb > 17
> >                 return Float16.multiply(signum, Float16.POSITIVE_INFINITY);
> >             }
> >             if (scale < 0) {
> >                 return Float16.multiply(signum, valueOf(w.multiply(bigTenToThe(-scale))));
> >             }
> >             if (scale == 0) {
> >                 return Float16.multiply(signum, valueOf(w));
> >             }
> >             int ql = (int) qb - (P_F16 + 3);
> >             BigInteger pow10 =  bigTenToThe(scale);
> >             BigInteger m, n;
> >             if (ql <= 0) {
> >                 m = w.shiftLeft(-ql);
> >                 n = pow10;
> >             } else {
> >                 m = w;
> >                 n = pow10.shiftLeft(ql);
> >             }
> >             BigInteger[] qr = m.divideAndRemainder(n);
> >             /*
> >              * We have
> >              *      2^12 = 2^{P+1} <= i < 2^{P+5} = 2^16
> >              * Contrary to the double and float cases, where we use long and int, resp.,
> >              * here we cannot simply declare i as short, because P + 5 < Short.SIZE
> >              * fails to hold.
> >              * Using int is safe, though.
> >              *
> >              * Further, as Math.scalb(Float16) does not exists, we fall back to
> >              * Math.scalb(double).
> >              */
> >             int i = qr[0].intValue();
> >             int sb = qr[1].signum();
> >             int dq = (Integer.SIZE - (P_F16 + 2)) - Integer.numberOfLeadingZeros(i);
> >             int eq = (Q_MIN_F16 - 2) - ql;
> >             if (dq >= eq) {
> >                 return Float16.valueOf(bd.signum() * Math.scalb((double) (i | sb), ql));
> >             }
> >             int mask = (1 << eq) - 1;
> >             int j = i >> eq | (Integer.signum(i & mask)) | sb;
> >             return Float16.valueOf(bd.signum() * Math.scalb((double) j, Q_MIN_F16 - 2));
> >         }
> > 
> >         public static Float16 valueOf(BigInteger bi) {
> >             int signum = bi.signum();
> >             return (signum == 0 || bi.bitLength() <= 31)
> >                 ? Float16.valueOf(bi.longValue())  // might return infinities
> >                 : signum > 0
> >                 ? Float16.POSITIVE_INFINITY
> >                 : Float16.NEGATIVE_INFINITY;
> >         }
> 577,578c693
> <      * according to the IEEE 754 floating-point binary16 bit layout,
> <      * preserving Not-a-Number (NaN) values.
> ---
> >      * according to the IEEE 754 floating-point binary16 bit layout.
> 591,607d705
> <      * Returns a representation of the specified floating-point value
> <      * according to the IEEE 754 floating-point binary16 bit layout.
> <      *
> <      * @param   fp16   a {@code Float16} floating-point number.
> <      * @return the bits that represent the floating-point number.
> <      *
> <      * @see Float#floatToIntBits(float)
> <      * @see Double#doubleToLongBits(double)
> <      */
> <     public static short float16ToShortBits(Float16 fp16) {
> <         if (!isNaN(fp16)) {
> <             return float16ToRawShortBits(fp16);
> <         }
> <         return 0x7e00;
> <     }
> < 
> <     /**
> 694c792
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 714c812
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 783c881
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 806c904
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 829c927
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 852c950
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 873c971
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 907c1005
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 1109c1207
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 1131c1229
> <     @IntrinsicCandidate
> ---
> >     // @IntrinsicCandidate
> 1319a1418
> >         int DoubleConsts_EXP_BIAS = 1023;
> 1328c1427
> <                 * Double.longBitsToDouble((long) (scaleFactor + DoubleConsts.EXP_BIAS) << Double.PRECISION - 1));
> ---
> >                 * Double.longBitsToDouble((long) (scaleFactor + DoubleConsts_EXP_BIAS) << Double.PRECISION - 1));
> 1330d1428
> < 
> 1374d1471
> < 
> ```

As initially done here, the port of Float16 omits any VM intrinsification; that would need to be done by subsequent work. Subsequent work may also adapt the Java implementations to be more amenable to being intrinsified.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420858962
PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420867094


More information about the core-libs-dev mailing list