From 95b52fd187564cabceb3309daa4ee6ddf697de58 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 24 Feb 2017 18:44:12 -0800 Subject: [PATCH] Make Half look like and behave like a boxed instance Bug: 35765416 Test: HalfTest Change-Id: I7ef52428f8b4e2c05b91d7eb37cc4cb5ecc5c6b9 --- api/current.txt | 25 +- api/system-current.txt | 25 +- api/test-current.txt | 25 +- core/java/android/annotation/HalfFloat.java | 2 +- core/java/android/util/Half.java | 396 +++++++++++++++++++- graphics/java/android/graphics/Color.java | 6 +- 6 files changed, 462 insertions(+), 17 deletions(-) diff --git a/api/current.txt b/api/current.txt index 7f7474c539b02..c0a9faec30c61 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42369,30 +42369,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf diff --git a/api/system-current.txt b/api/system-current.txt index 4aca489e49ab8..b0da2f7fc9339 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -45874,30 +45874,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf diff --git a/api/test-current.txt b/api/test-current.txt index 9f7b8e903619f..77829435aecd5 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42558,30 +42558,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf diff --git a/core/java/android/annotation/HalfFloat.java b/core/java/android/annotation/HalfFloat.java index d3e9f08dee9e2..256008c5e31f1 100644 --- a/core/java/android/annotation/HalfFloat.java +++ b/core/java/android/annotation/HalfFloat.java @@ -37,7 +37,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * } * * @see android.util.Half - * @see android.util.Half#valueOf(float) + * @see android.util.Half#toHalf(float) * @see android.util.Half#toFloat(short) * * @hide diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index e4f8dd1b12b83..84c2e8302e898 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -17,9 +17,13 @@ package android.util; import android.annotation.HalfFloat; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import sun.misc.FloatingDecimal; /** - *

Half is a utility class to manipulate half-precision 16-bit + *

The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit * IEEE 754 * floating point data types (also called fp16 or binary16). A half-precision float can be * created from or converted to single-precision floats, and is stored in a short data type. @@ -88,7 +92,7 @@ import android.annotation.HalfFloat; *

This table shows that numbers higher than 1024 lose all fractional precision.

*/ @SuppressWarnings("SimplifiableIfStatement") -public final class Half { +public final class Half extends Number implements Comparable { /** * The number of bits used to represent a half-precision float value. */ @@ -164,7 +168,332 @@ public final class Half { private static final int FP32_DENORMAL_MAGIC = 126 << 23; private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); - private Half() { + private final @HalfFloat short mValue; + + /** + * Constructs a newly allocated {@code Half} object that represents the + * half-precision float type argument. + * + * @param value The value to be represented by the {@code Half} + */ + public Half(@HalfFloat short value) { + mValue = value; + } + + /** + * Constructs a newly allocated {@code Half} object that represents the + * argument converted to a half-precision float. + * + * @param value The value to be represented by the {@code Half} + * + * @see #toHalf(float) + */ + public Half(float value) { + mValue = toHalf(value); + } + + /** + * Constructs a newly allocated {@code Half} object that + * represents the argument converted to a half-precision float. + * + * @param value The value to be represented by the {@code Half} + * + * @see #toHalf(float) + */ + public Half(double value) { + mValue = toHalf((float) value); + } + + /** + *

Constructs a newly allocated {@code Half} object that represents the + * half-precision float value represented by the string. + * The string is converted to a half-precision float value as if by the + * {@link #valueOf(String)} method.

+ * + *

Calling this constructor is equivalent to calling:

+ *
+     *     new Half(Float.parseFloat(value))
+     * 
+ * + * @param value A string to be converted to a {@code Half} + * @throws NumberFormatException if the string does not contain a parsable number + * + * @see Float#valueOf(java.lang.String) + * @see #toHalf(float) + */ + public Half(@NonNull String value) throws NumberFormatException { + mValue = toHalf(Float.parseFloat(value)); + } + + /** + * Returns the half-precision value of this {@code Half} as a {@code short} + * containing the bit representation described in {@link Half}. + * + * @return The half-precision float value represented by this object + */ + public @HalfFloat short halfValue() { + return mValue; + } + + /** + * Returns the value of this {@code Half} as a {@code byte} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code byte} + */ + @Override + public byte byteValue() { + return (byte) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code short} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code short} + */ + @Override + public short shortValue() { + return (short) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code int} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code int} + */ + @Override + public int intValue() { + return (int) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code long} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code long} + */ + @Override + public long longValue() { + return (long) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code float} after + * a widening primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code float} + */ + @Override + public float floatValue() { + return toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code double} after + * a widening primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code double} + */ + @Override + public double doubleValue() { + return toFloat(mValue); + } + + /** + * Returns true if this {@code Half} value represents a Not-a-Number, + * false otherwise. + * + * @return True if the value is a NaN, false otherwise + */ + public boolean isNaN() { + return isNaN(mValue); + } + + /** + * Compares this object against the specified object. The result is {@code true} + * if and only if the argument is not {@code null} and is a {@code Half} object + * that represents the same half-precision value as the this object. Two + * half-precision values are considered to be the same if and only if the method + * {@link #halfToIntBits(short)} returns an identical {@code int} value for both. + * + * @param o The object to compare + * @return True if the objects are the same, false otherwise + * + * @see #halfToIntBits(short) + */ + @Override + public boolean equals(@Nullable Object o) { + return (o instanceof Half) && + (halfToIntBits(((Half) o).mValue) == halfToIntBits(mValue)); + } + + /** + * Returns a hash code for this {@code Half} object. The result is the + * integer bit representation, exactly as produced by the method + * {@link #halfToIntBits(short)}, of the primitive half-precision float + * value represented by this {@code Half} object. + * + * @return A hash code value for this object + */ + @Override + public int hashCode() { + return hashCode(mValue); + } + + /** + * Returns a string representation of the specified half-precision + * float value. See {@link #toString(short)} for more information. + * + * @return A string representation of this {@code Half} object + */ + @NonNull + @Override + public String toString() { + return toString(mValue); + } + + /** + *

Compares the two specified half-precision float values. The following + * conditions apply during the comparison:

+ * + * + * + * @param h The half-precision float value to compare to the half-precision value + * represented by this {@code Half} object + * + * @return The value {@code 0} if {@code x} is numerically equal to {@code y}; a + * value less than {@code 0} if {@code x} is numerically less than {@code y}; + * and a value greater than {@code 0} if {@code x} is numerically greater + * than {@code y} + */ + @Override + public int compareTo(@NonNull Half h) { + return compare(mValue, h.mValue); + } + + /** + * Returns a hash code for a half-precision float value. + * + * @param h The value to hash + * + * @return A hash code value for a half-precision float value + */ + public static int hashCode(@HalfFloat short h) { + return halfToIntBits(h); + } + + /** + *

Compares the two specified half-precision float values. The following + * conditions apply during the comparison:

+ * + * + * + * @param x The first half-precision float value to compare. + * @param y The second half-precision float value to compare + * + * @return The value {@code 0} if {@code x} is numerically equal to {@code y}, a + * value less than {@code 0} if {@code x} is numerically less than {@code y}, + * and a value greater than {@code 0} if {@code x} is numerically greater + * than {@code y} + */ + public static int compare(@HalfFloat short x, @HalfFloat short y) { + if (less(x, y)) return -1; + 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)); + } + + /** + *

Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.

+ * + *

Similar to {@link #halfToIntBits(short)}, this method collapses all + * possible Not-a-Number values to a single canonical Not-a-Number value + * defined by {@link #NaN}.

+ * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToIntBits(short) + */ + public static @HalfFloat short halfToShortBits(@HalfFloat short h) { + return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h; + } + + /** + *

Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.

+ * + *

Unlike {@link #halfToRawIntBits(short)}, this method collapses all + * possible Not-a-Number values to a single canonical Not-a-Number value + * defined by {@link #NaN}.

+ * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToRawIntBits(short) + * @see #halfToShortBits(short) + * @see #intBitsToHalf(int) + */ + public static int halfToIntBits(@HalfFloat short h) { + return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff; + } + + /** + *

Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.

+ * + *

The argument is considered to be a representation of a half-precision + * float value according to the bit layout described in {@link Half}. The 16 + * most significant bits of the returned value are set to 0.

+ * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToIntBits(short) + * @see #intBitsToHalf(int) + */ + public static int halfToRawIntBits(@HalfFloat short h) { + return h & 0xffff; + } + + /** + *

Returns the half-precision float value corresponding to a given + * bit representation.

+ * + *

The argument is considered to be a representation of a half-precision + * float value according to the bit layout described in {@link Half}. The 16 + * most significant bits of the argument are ignored.

+ * + * @param bits An integer + * @return The half-precision float value with the same bit pattern + */ + public static @HalfFloat short intBitsToHalf(int bits) { + return (short) (bits & 0xffff); } /** @@ -509,7 +838,7 @@ public final class Half { * infinity, false otherwise. * * @param h A half-precision float value - * @return true if the value is positive infinity or negative infinity, + * @return True if the value is positive infinity or negative infinity, * false otherwise */ public static boolean isInfinite(@HalfFloat short h) { @@ -521,7 +850,7 @@ public final class Half { * a Not-a-Number, false otherwise. * * @param h A half-precision float value - * @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) { return (h & FP16_COMBINED) > FP16_EXPONENT_MAX; @@ -535,7 +864,7 @@ public final class Half { * number, this method returns false. * * @param h A half-precision float value - * @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) { return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX; @@ -608,7 +937,7 @@ public final class Half { * @return A half-precision float value */ @SuppressWarnings("StatementWithEmptyBody") - public static @HalfFloat short valueOf(float f) { + public static @HalfFloat short toHalf(float f) { int bits = Float.floatToRawIntBits(f); int s = (bits >>> FP32_SIGN_SHIFT ); int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK; @@ -649,6 +978,57 @@ public final class Half { return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM); } + /** + * Returns a {@code Half} instance representing the specified + * half-precision float value. + * + * @param h A half-precision float value + * @return a {@code Half} instance representing {@code h} + */ + public static @NonNull Half valueOf(@HalfFloat short h) { + return new Half(h); + } + + /** + * Returns a {@code Half} instance representing the specified float value. + * + * @param f A float value + * @return a {@code Half} instance representing {@code f} + */ + public static @NonNull Half valueOf(float f) { + return new Half(f); + } + + /** + * Returns a {@code Half} instance representing the specified string value. + * Calling this method is equivalent to calling + * toHalf(Float.parseString(h)). See {@link Float#valueOf(String)} + * for more information on the format of the string representation. + * + * @param s The string to be parsed + * @return a {@code Half} instance representing {@code h} + * @throws NumberFormatException if the string does not contain a parsable + * half-precision float value + */ + public static @NonNull Half valueOf(@NonNull String s) { + return new Half(s); + } + + /** + * Returns the half-precision float value represented by the specified string. + * Calling this method is equivalent to calling + * toHalf(Float.parseString(h)). See {@link Float#valueOf(String)} + * for more information on the format of the string representation. + * + * @param s The string to be parsed + * @return A half-precision float value represented by the string + * @throws NumberFormatException if the string does not contain a parsable + * half-precision float value + */ + public static @HalfFloat short parseHalf(@NonNull String s) throws NumberFormatException { + return toHalf(FloatingDecimal.parseFloat(s)); + } + /** * Returns a string representation of the specified half-precision * float value. Calling this method is equivalent to calling @@ -658,6 +1038,7 @@ public final class Half { * @param h A half-precision float value * @return A string representation of the specified value */ + @NonNull public static String toString(@HalfFloat short h) { return Float.toString(toFloat(h)); } @@ -688,6 +1069,7 @@ public final class Half { * @param h A half-precision float value * @return A hexadecimal string representation of the specified value */ + @NonNull public static String toHexString(@HalfFloat short h) { StringBuilder o = new StringBuilder(); diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index ff21cac85a239..33d19d4063f5d 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -1023,9 +1023,9 @@ public class Color { "The color space must use a color model with at most 3 components"); } - @HalfFloat short r = Half.valueOf(red); - @HalfFloat short g = Half.valueOf(green); - @HalfFloat short b = Half.valueOf(blue); + @HalfFloat short r = Half.toHalf(red); + @HalfFloat short g = Half.toHalf(green); + @HalfFloat short b = Half.toHalf(blue); int a = (int) (Math.max(0.0f, Math.min(alpha, 1.0f)) * 1023.0f + 0.5f);