On Thu, 17 Oct 2024 23:37:54 GMT, Joe Darcy <da...@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