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