Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6bf381b5 authored by Hans Boehm's avatar Hans Boehm Committed by Gerrit Code Review
Browse files

Merge "Move Half implementations to libcore to allow ART optimizations"

parents 1261967c e008d965
Loading
Loading
Loading
Loading
+33 −241
Original line number Original line Diff line number Diff line
@@ -20,6 +20,8 @@ import android.annotation.HalfFloat;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;


import libcore.util.FP16;

/**
/**
 * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
 * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
 * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
 * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -148,25 +150,6 @@ public final class Half extends Number implements Comparable<Half> {
     */
     */
    public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;
    public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;


    private static final int FP16_SIGN_SHIFT        = 15;
    private static final int FP16_SIGN_MASK         = 0x8000;
    private static final int FP16_EXPONENT_SHIFT    = 10;
    private static final int FP16_EXPONENT_MASK     = 0x1f;
    private static final int FP16_SIGNIFICAND_MASK  = 0x3ff;
    private static final int FP16_EXPONENT_BIAS     = 15;
    private static final int FP16_COMBINED          = 0x7fff;
    private static final int FP16_EXPONENT_MAX      = 0x7c00;

    private static final int FP32_SIGN_SHIFT        = 31;
    private static final int FP32_EXPONENT_SHIFT    = 23;
    private static final int FP32_EXPONENT_MASK     = 0xff;
    private static final int FP32_SIGNIFICAND_MASK  = 0x7fffff;
    private static final int FP32_EXPONENT_BIAS     = 127;
    private static final int FP32_QNAN_MASK         = 0x400000;

    private static final int FP32_DENORMAL_MAGIC = 126 << 23;
    private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);

    private final @HalfFloat short mValue;
    private final @HalfFloat short mValue;


    /**
    /**
@@ -414,16 +397,7 @@ public final class Half extends Number implements Comparable<Half> {
     *          than {@code y}
     *          than {@code y}
     */
     */
    public static int compare(@HalfFloat short x, @HalfFloat short y) {
    public static int compare(@HalfFloat short x, @HalfFloat short y) {
        if (less(x, y)) return -1;
        return FP16.compare(x, y);
        if (greater(x, y)) return 1;

        // Collapse NaNs, akin to halfToIntBits(), but we want to keep
        // (signed) short value types to preserve the ordering of -0.0
        // and +0.0
        short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x;
        short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y;

        return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
    }
    }


    /**
    /**
@@ -440,7 +414,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @see #halfToIntBits(short)
     * @see #halfToIntBits(short)
     */
     */
    public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
    public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h;
        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h;
    }
    }


    /**
    /**
@@ -459,7 +433,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @see #intBitsToHalf(int)
     * @see #intBitsToHalf(int)
     */
     */
    public static int halfToIntBits(@HalfFloat short h) {
    public static int halfToIntBits(@HalfFloat short h) {
        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff;
        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff;
    }
    }


    /**
    /**
@@ -505,7 +479,7 @@ public final class Half extends Number implements Comparable<Half> {
     *         of the second parameter
     *         of the second parameter
     */
     */
    public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
    public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
        return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
        return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK));
    }
    }


    /**
    /**
@@ -523,7 +497,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return The absolute value of the specified half-precision float
     * @return The absolute value of the specified half-precision float
     */
     */
    public static @HalfFloat short abs(@HalfFloat short h) {
    public static @HalfFloat short abs(@HalfFloat short h) {
        return (short) (h & FP16_COMBINED);
        return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK);
    }
    }


    /**
    /**
@@ -538,26 +512,18 @@ public final class Half extends Number implements Comparable<Half> {
     * the result is zero (with the same sign)</li>
     * the result is zero (with the same sign)</li>
     * </ul>
     * </ul>
     *
     *
     * <p class=note>
     * <strong>Note:</strong> Unlike the identically named
     * <code class=prettyprint>int java.lang.Math.round(float)</code> method,
     * this returns a Half value stored in a short, <strong>not</strong> an
     * actual short integer result.
     *
     * @param h A half-precision float value
     * @param h A half-precision float value
     * @return The value of the specified half-precision float rounded to the nearest
     * @return The value of the specified half-precision float rounded to the nearest
     *         half-precision float value
     *         half-precision float value
     */
     */
    public static @HalfFloat short round(@HalfFloat short h) {
    public static @HalfFloat short round(@HalfFloat short h) {
        int bits = h & 0xffff;
        return FP16.rint(h);
        int e = bits & 0x7fff;
        int result = bits;

        if (e < 0x3c00) {
            result &= FP16_SIGN_MASK;
            result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0));
        } else if (e < 0x6400) {
            e = 25 - (e >> 10);
            int mask = (1 << e) - 1;
            result += (1 << (e - 1));
            result &= ~mask;
        }

        return (short) result;
    }
    }


    /**
    /**
@@ -577,21 +543,7 @@ public final class Half extends Number implements Comparable<Half> {
     *         greater than or equal to the specified half-precision float value
     *         greater than or equal to the specified half-precision float value
     */
     */
    public static @HalfFloat short ceil(@HalfFloat short h) {
    public static @HalfFloat short ceil(@HalfFloat short h) {
        int bits = h & 0xffff;
        return FP16.ceil(h);
        int e = bits & 0x7fff;
        int result = bits;

        if (e < 0x3c00) {
            result &= FP16_SIGN_MASK;
            result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0));
        } else if (e < 0x6400) {
            e = 25 - (e >> 10);
            int mask = (1 << e) - 1;
            result += mask & ((bits >> 15) - 1);
            result &= ~mask;
        }

        return (short) result;
    }
    }


    /**
    /**
@@ -611,21 +563,7 @@ public final class Half extends Number implements Comparable<Half> {
     *         less than or equal to the specified half-precision float value
     *         less than or equal to the specified half-precision float value
     */
     */
    public static @HalfFloat short floor(@HalfFloat short h) {
    public static @HalfFloat short floor(@HalfFloat short h) {
        int bits = h & 0xffff;
        return FP16.floor(h);
        int e = bits & 0x7fff;
        int result = bits;

        if (e < 0x3c00) {
            result &= FP16_SIGN_MASK;
            result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
        } else if (e < 0x6400) {
            e = 25 - (e >> 10);
            int mask = (1 << e) - 1;
            result += mask & -(bits >> 15);
            result &= ~mask;
        }

        return (short) result;
    }
    }


    /**
    /**
@@ -644,19 +582,7 @@ public final class Half extends Number implements Comparable<Half> {
     *         half-precision float value
     *         half-precision float value
     */
     */
    public static @HalfFloat short trunc(@HalfFloat short h) {
    public static @HalfFloat short trunc(@HalfFloat short h) {
        int bits = h & 0xffff;
        return FP16.trunc(h);
        int e = bits & 0x7fff;
        int result = bits;

        if (e < 0x3c00) {
            result &= FP16_SIGN_MASK;
        } else if (e < 0x6400) {
            e = 25 - (e >> 10);
            int mask = (1 << e) - 1;
            result &= ~mask;
        }

        return (short) result;
    }
    }


    /**
    /**
@@ -672,15 +598,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return The smaller of the two specified half-precision values
     * @return The smaller of the two specified half-precision values
     */
     */
    public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
    public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
        return FP16.min(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;

        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
            return (x & FP16_SIGN_MASK) != 0 ? x : y;
        }

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
    }
    }


    /**
    /**
@@ -697,15 +615,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return The larger of the two specified half-precision values
     * @return The larger of the two specified half-precision values
     */
     */
    public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
    public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
        return FP16.max(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;

        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
            return (x & FP16_SIGN_MASK) != 0 ? y : x;
        }

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
    }
    }


    /**
    /**
@@ -719,11 +629,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if x is less than y, false otherwise
     * @return True if x is less than y, false otherwise
     */
     */
    public static boolean less(@HalfFloat short x, @HalfFloat short y) {
    public static boolean less(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
        return FP16.less(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
    }
    }


    /**
    /**
@@ -737,11 +643,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if x is less than or equal to y, false otherwise
     * @return True if x is less than or equal to y, false otherwise
     */
     */
    public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
    public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
        return FP16.lessEquals(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
    }
    }


    /**
    /**
@@ -755,11 +657,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if x is greater than y, false otherwise
     * @return True if x is greater than y, false otherwise
     */
     */
    public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
    public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
        return FP16.greater(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
    }
    }


    /**
    /**
@@ -773,11 +671,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if x is greater than y, false otherwise
     * @return True if x is greater than y, false otherwise
     */
     */
    public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
    public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
        return FP16.greaterEquals(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;

        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
    }
    }


    /**
    /**
@@ -791,10 +685,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if x is equal to y, false otherwise
     * @return True if x is equal to y, false otherwise
     */
     */
    public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
    public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
        return FP16.equals(x, y);
        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;

        return x == y || ((x | y) & FP16_COMBINED) == 0;
    }
    }


    /**
    /**
@@ -804,7 +695,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return 1 if the value is positive, -1 if the value is negative
     * @return 1 if the value is positive, -1 if the value is negative
     */
     */
    public static int getSign(@HalfFloat short h) {
    public static int getSign(@HalfFloat short h) {
        return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
        return (h & FP16.SIGN_MASK) == 0 ? 1 : -1;
    }
    }


    /**
    /**
@@ -818,7 +709,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return The unbiased exponent of the specified value
     * @return The unbiased exponent of the specified value
     */
     */
    public static int getExponent(@HalfFloat short h) {
    public static int getExponent(@HalfFloat short h) {
        return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
        return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS;
    }
    }


    /**
    /**
@@ -829,7 +720,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return The significand, or significand, of the specified vlaue
     * @return The significand, or significand, of the specified vlaue
     */
     */
    public static int getSignificand(@HalfFloat short h) {
    public static int getSignificand(@HalfFloat short h) {
        return h & FP16_SIGNIFICAND_MASK;
        return h & FP16.SIGNIFICAND_MASK;
    }
    }


    /**
    /**
@@ -841,7 +732,7 @@ public final class Half extends Number implements Comparable<Half> {
     *         false otherwise
     *         false otherwise
     */
     */
    public static boolean isInfinite(@HalfFloat short h) {
    public static boolean isInfinite(@HalfFloat short h) {
        return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
        return FP16.isInfinite(h);
    }
    }


    /**
    /**
@@ -852,7 +743,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if the value is a NaN, false otherwise
     * @return True if the value is a NaN, false otherwise
     */
     */
    public static boolean isNaN(@HalfFloat short h) {
    public static boolean isNaN(@HalfFloat short h) {
        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
        return FP16.isNaN(h);
    }
    }


    /**
    /**
@@ -866,7 +757,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return True if the value is normalized, false otherwise
     * @return True if the value is normalized, false otherwise
     */
     */
    public static boolean isNormalized(@HalfFloat short h) {
    public static boolean isNormalized(@HalfFloat short h) {
        return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX;
        return FP16.isNormalized(h);
    }
    }


    /**
    /**
@@ -885,35 +776,7 @@ public final class Half extends Number implements Comparable<Half> {
     * @return A normalized single-precision float value
     * @return A normalized single-precision float value
     */
     */
    public static float toFloat(@HalfFloat short h) {
    public static float toFloat(@HalfFloat short h) {
        int bits = h & 0xffff;
        return FP16.toFloat(h);
        int s = bits & FP16_SIGN_MASK;
        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;

        int outE = 0;
        int outM = 0;

        if (e == 0) { // Denormal or 0
            if (m != 0) {
                // Convert denorm fp16 into normalized fp32
                float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
                o -= FP32_DENORMAL_FLOAT;
                return s == 0 ? o : -o;
            }
        } else {
            outM = m << 13;
            if (e == 0x1f) { // Infinite or NaN
                outE = 0xff;
                if (outM != 0) { // SNaNs are quieted
                    outM |= FP32_QNAN_MASK;
                }
            } else {
                outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
            }
        }

        int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
        return Float.intBitsToFloat(out);
    }
    }


    /**
    /**
@@ -940,44 +803,7 @@ public final class Half extends Number implements Comparable<Half> {
     */
     */
    @SuppressWarnings("StatementWithEmptyBody")
    @SuppressWarnings("StatementWithEmptyBody")
    public static @HalfFloat short toHalf(float f) {
    public static @HalfFloat short toHalf(float f) {
        int bits = Float.floatToRawIntBits(f);
        return FP16.toHalf(f);
        int s = (bits >>> FP32_SIGN_SHIFT    );
        int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
        int m = (bits                        ) & FP32_SIGNIFICAND_MASK;

        int outE = 0;
        int outM = 0;

        if (e == 0xff) { // Infinite or NaN
            outE = 0x1f;
            outM = m != 0 ? 0x200 : 0;
        } else {
            e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
            if (e >= 0x1f) { // Overflow
                outE = 0x31;
            } else if (e <= 0) { // Underflow
                if (e < -10) {
                    // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
                } else {
                    // The fp32 value is a normalized float less than MIN_NORMAL,
                    // we convert to a denorm fp16
                    m = (m | 0x800000) >> (1 - e);
                    if ((m & 0x1000) != 0) m += 0x2000;
                    outM = m >> 13;
                }
            } else {
                outE = e;
                outM = m >> 13;
                if ((m & 0x1000) != 0) {
                    // Round to nearest "0.5" up
                    int out = (outE << FP16_EXPONENT_SHIFT) | outM;
                    out++;
                    return (short) (out | (s << FP16_SIGN_SHIFT));
                }
            }
        }

        return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM);
    }
    }


    /**
    /**
@@ -1073,40 +899,6 @@ public final class Half extends Number implements Comparable<Half> {
     */
     */
    @NonNull
    @NonNull
    public static String toHexString(@HalfFloat short h) {
    public static String toHexString(@HalfFloat short h) {
        StringBuilder o = new StringBuilder();
        return FP16.toHexString(h);

        int bits = h & 0xffff;
        int s = (bits >>> FP16_SIGN_SHIFT    );
        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;

        if (e == 0x1f) { // Infinite or NaN
            if (m == 0) {
                if (s != 0) o.append('-');
                o.append("Infinity");
            } else {
                o.append("NaN");
            }
        } else {
            if (s == 1) o.append('-');
            if (e == 0) {
                if (m == 0) {
                    o.append("0x0.0p0");
                } else {
                    o.append("0x0.");
                    String significand = Integer.toHexString(m);
                    o.append(significand.replaceFirst("0{2,}$", ""));
                    o.append("p-14");
                }
            } else {
                o.append("0x1.");
                String significand = Integer.toHexString(m);
                o.append(significand.replaceFirst("0{2,}$", ""));
                o.append('p');
                o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
            }
        }

        return o.toString();
    }
    }
}
}