RFR: 8341260: Add Float16 to jdk.incubator.vector
Joe Darcy
darcy at openjdk.org
Sat Oct 19 21:02:53 UTC 2024
On Thu, 17 Oct 2024 23:11:07 GMT, Joe Darcy <darcy at openjdk.org> wrote:
> Port of Float16 from java.lang in the lworld+fp16 branch to jdk.incubabor.vector.
Add as contributors other engineers who worked on Float16 on the lworld+fp16 branch in Valhalla.
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
<
$ diff src/java.base/share/classes/jdk/internal/math/Float16Consts.java ~/jdk24/open/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java
26c26
< package jdk.internal.math;
---
> package jdk.incubator.vector;
28,30c28,30
< import static java.lang.Float16.MIN_EXPONENT;
< import static java.lang.Float16.PRECISION;
< import static java.lang.Float16.SIZE;
---
> import static jdk.incubator.vector.Float16.MIN_EXPONENT;
> import static jdk.incubator.vector.Float16.PRECISION;
> import static jdk.incubator.vector.Float16.SIZE;
37c37
< public class Float16Consts {
---
> class Float16Consts {
$ 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();
-------------
PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420835843
PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420843492
PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420846567
PR Comment: https://git.openjdk.org/jdk/pull/21574#issuecomment-2420850283
More information about the core-libs-dev
mailing list