content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.util;
  
  import android.annotation.HalfFloat;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  
  /**
   * <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>
   * 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.
   * To distinguish short values holding half-precision floats from regular short values,
   * it is recommended to use the <code>@HalfFloat</code> annotation.</p>
   *
   * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p>
   * <ul>
   * <li>Sign bit: 1 bit</li>
   * <li>Exponent width: 5 bits</li>
   * <li>Significand: 10 bits</li>
   * </ul>
   *
   * <p>The format is laid out as follows:</p>
   * <pre>
   * 1   11111   1111111111
   * ^   --^--   -----^----
   * sign  |          |_______ significand
   *       |
   *       -- exponent
   * </pre>
   *
   * <p>Half-precision floating points can be useful to save memory and/or
   * bandwidth at the expense of range and precision when compared to single-precision
   * floating points (fp32).</p>
   * <p>To help you decide whether fp16 is the right storage type for you need, please
   * refer to the table below that shows the available precision throughout the range of
   * possible values. The <em>precision</em> column indicates the step size between two
   * consecutive numbers in a specific part of the range.</p>
   *
   * <table summary="Precision of fp16 across the range">
   *     <tr><th>Range start</th><th>Precision</th></tr>
   *     <tr><td>0</td><td>1 &frasl; 16,777,216</td></tr>
   *     <tr><td>1 &frasl; 16,384</td><td>1 &frasl; 16,777,216</td></tr>
   *     <tr><td>1 &frasl; 8,192</td><td>1 &frasl; 8,388,608</td></tr>
   *     <tr><td>1 &frasl; 4,096</td><td>1 &frasl; 4,194,304</td></tr>
   *     <tr><td>1 &frasl; 2,048</td><td>1 &frasl; 2,097,152</td></tr>
   *     <tr><td>1 &frasl; 1,024</td><td>1 &frasl; 1,048,576</td></tr>
   *     <tr><td>1 &frasl; 512</td><td>1 &frasl; 524,288</td></tr>
   *     <tr><td>1 &frasl; 256</td><td>1 &frasl; 262,144</td></tr>
   *     <tr><td>1 &frasl; 128</td><td>1 &frasl; 131,072</td></tr>
   *     <tr><td>1 &frasl; 64</td><td>1 &frasl; 65,536</td></tr>
   *     <tr><td>1 &frasl; 32</td><td>1 &frasl; 32,768</td></tr>
   *     <tr><td>1 &frasl; 16</td><td>1 &frasl; 16,384</td></tr>
   *     <tr><td>1 &frasl; 8</td><td>1 &frasl; 8,192</td></tr>
   *     <tr><td>1 &frasl; 4</td><td>1 &frasl; 4,096</td></tr>
   *     <tr><td>1 &frasl; 2</td><td>1 &frasl; 2,048</td></tr>
   *     <tr><td>1</td><td>1 &frasl; 1,024</td></tr>
   *     <tr><td>2</td><td>1 &frasl; 512</td></tr>
   *     <tr><td>4</td><td>1 &frasl; 256</td></tr>
   *     <tr><td>8</td><td>1 &frasl; 128</td></tr>
   *     <tr><td>16</td><td>1 &frasl; 64</td></tr>
   *     <tr><td>32</td><td>1 &frasl; 32</td></tr>
   *     <tr><td>64</td><td>1 &frasl; 16</td></tr>
   *     <tr><td>128</td><td>1 &frasl; 8</td></tr>
   *     <tr><td>256</td><td>1 &frasl; 4</td></tr>
   *     <tr><td>512</td><td>1 &frasl; 2</td></tr>
   *     <tr><td>1,024</td><td>1</td></tr>
   *     <tr><td>2,048</td><td>2</td></tr>
   *     <tr><td>4,096</td><td>4</td></tr>
   *     <tr><td>8,192</td><td>8</td></tr>
   *     <tr><td>16,384</td><td>16</td></tr>
   *     <tr><td>32,768</td><td>32</td></tr>
   * </table>
   *
   * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
   */
  @SuppressWarnings("SimplifiableIfStatement")
  public final class Half extends Number implements Comparable<Half> {
      /**
       * The number of bits used to represent a half-precision float value.
       */
      public static final int SIZE = 16;
  
      /**
       * Epsilon is the difference between 1.0 and the next value representable
       * by a half-precision floating-point.
       */
      public static final @HalfFloat short EPSILON = (short) 0x1400;
  
      /**
       * Maximum exponent a finite half-precision float may have.
       */
      public static final int MAX_EXPONENT = 15;
      /**
       * Minimum exponent a normalized half-precision float may have.
       */
      public static final int MIN_EXPONENT = -14;
  
      /**
       * Smallest negative value a half-precision float may have.
       */
      public static final @HalfFloat short LOWEST_VALUE = (short) 0xfbff;
      /**
       * Maximum positive finite value a half-precision float may have.
       */
      public static final @HalfFloat short MAX_VALUE = (short) 0x7bff;
      /**
       * Smallest positive normal value a half-precision float may have.
       */
      public static final @HalfFloat short MIN_NORMAL = (short) 0x0400;
      /**
       * Smallest positive non-zero value a half-precision float may have.
       */
      public static final @HalfFloat short MIN_VALUE = (short) 0x0001;
      /**
       * A Not-a-Number representation of a half-precision float.
       */
      public static final @HalfFloat short NaN = (short) 0x7e00;
      /**
       * Negative infinity of type half-precision float.
       */
      public static final @HalfFloat short NEGATIVE_INFINITY = (short) 0xfc00;
      /**
       * Negative 0 of type half-precision float.
       */
      public static final @HalfFloat short NEGATIVE_ZERO = (short) 0x8000;
      /**
       * Positive infinity of type half-precision float.
       */
      public static final @HalfFloat short POSITIVE_INFINITY = (short) 0x7c00;
      /**
       * Positive 0 of type half-precision float.
       */
      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;
  
      /**
       * 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);
      }
  
      /**
       * <p>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.</p>
       *
       * <p>Calling this constructor is equivalent to calling:</p>
       * <pre>
       *     new Half(Float.parseFloat(value))
       * </pre>
       *
       * @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);
      }
  
      /**
       * <p>Compares the two specified half-precision float values. The following
       * conditions apply during the comparison:</p>
       *
       * <ul>
       * <li>{@link #NaN} is considered by this method to be equal to itself and greater
       * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li>
       * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than
       * {@link #NEGATIVE_ZERO}.</li>
       * </ul>
       *
       * @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);
      }
  
      /**
       * <p>Compares the two specified half-precision float values. The following
       * conditions apply during the comparison:</p>
       *
       * <ul>
       * <li>{@link #NaN} is considered by this method to be equal to itself and greater
       * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li>
       * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than
       * {@link #NEGATIVE_ZERO}.</li>
       * </ul>
       *
       * @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));
      }
  
      /**
       * <p>Returns a representation of the specified half-precision float value
       * according to the bit layout described in {@link Half}.</p>
       *
       * <p>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}.</p>
       *
       * @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;
      }
  
      /**
       * <p>Returns a representation of the specified half-precision float value
       * according to the bit layout described in {@link Half}.</p>
       *
       * <p>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}.</p>
       *
       * @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;
      }
  
      /**
       * <p>Returns a representation of the specified half-precision float value
       * according to the bit layout described in {@link Half}.</p>
       *
       * <p>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.</p>
       *
       * @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;
      }
  
      /**
       * <p>Returns the half-precision float value corresponding to a given
       * bit representation.</p>
       *
       * <p>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.</p>
       *
       * @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);
      }
  
      /**
       * Returns the first parameter with the sign of the second parameter.
       * This method treats NaNs as having a sign.
       *
       * @param magnitude A half-precision float value providing the magnitude of the result
       * @param sign  A half-precision float value providing the sign of the result
       * @return A value with the magnitude of the first parameter and the sign
       *         of the second parameter
       */
      public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
          return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
      }
  
      /**
       * Returns the absolute value of the specified half-precision float.
       * Special values are handled in the following ways:
       * <ul>
       * <li>If the specified half-precision float is NaN, the result is NaN</li>
       * <li>If the specified half-precision float is zero (negative or positive),
       * the result is positive zero (see {@link #POSITIVE_ZERO})</li>
       * <li>If the specified half-precision float is infinity (negative or positive),
       * the result is positive infinity (see {@link #POSITIVE_INFINITY})</li>
       * </ul>
       *
       * @param h A half-precision float value
       * @return The absolute value of the specified half-precision float
       */
      public static @HalfFloat short abs(@HalfFloat short h) {
          return (short) (h & FP16_COMBINED);
      }
  
      /**
       * Returns the closest integral half-precision float value to the specified
       * half-precision float value. Special values are handled in the
       * following ways:
       * <ul>
       * <li>If the specified half-precision float is NaN, the result is NaN</li>
       * <li>If the specified half-precision float is infinity (negative or positive),
       * the result is infinity (with the same sign)</li>
       * <li>If the specified half-precision float is zero (negative or positive),
       * the result is zero (with the same sign)</li>
       * </ul>
       *
       * @param h A half-precision float value
       * @return The value of the specified half-precision float rounded to the nearest
       *         half-precision float value
       */
      public static @HalfFloat short round(@HalfFloat short h) {
          int bits = h & 0xffff;
          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;
      }
  
      /**
       * Returns the smallest half-precision float value toward negative infinity
       * greater than or equal to the specified half-precision float value.
       * Special values are handled in the following ways:
       * <ul>
       * <li>If the specified half-precision float is NaN, the result is NaN</li>
       * <li>If the specified half-precision float is infinity (negative or positive),
       * the result is infinity (with the same sign)</li>
       * <li>If the specified half-precision float is zero (negative or positive),
       * the result is zero (with the same sign)</li>
       * </ul>
       *
       * @param h A half-precision float value
       * @return The smallest half-precision float value toward negative infinity
       *         greater than or equal to the specified half-precision float value
       */
      public static @HalfFloat short ceil(@HalfFloat short h) {
          int bits = h & 0xffff;
          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;
      }
  
      /**
       * Returns the largest half-precision float value toward positive infinity
       * less than or equal to the specified half-precision float value.
       * Special values are handled in the following ways:
       * <ul>
       * <li>If the specified half-precision float is NaN, the result is NaN</li>
       * <li>If the specified half-precision float is infinity (negative or positive),
       * the result is infinity (with the same sign)</li>
       * <li>If the specified half-precision float is zero (negative or positive),
       * the result is zero (with the same sign)</li>
       * </ul>
       *
       * @param h A half-precision float value
       * @return The largest half-precision float value toward positive infinity
       *         less than or equal to the specified half-precision float value
       */
      public static @HalfFloat short floor(@HalfFloat short h) {
          int bits = h & 0xffff;
          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;
      }
  
      /**
       * Returns the truncated half-precision float value of the specified
       * half-precision float value. Special values are handled in the following ways:
       * <ul>
       * <li>If the specified half-precision float is NaN, the result is NaN</li>
       * <li>If the specified half-precision float is infinity (negative or positive),
       * the result is infinity (with the same sign)</li>
       * <li>If the specified half-precision float is zero (negative or positive),
       * the result is zero (with the same sign)</li>
       * </ul>
       *
       * @param h A half-precision float value
       * @return The truncated half-precision float value of the specified
       *         half-precision float value
       */
      public static @HalfFloat short trunc(@HalfFloat short h) {
          int bits = h & 0xffff;
          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;
      }
  
      /**
       * Returns the smaller of two half-precision float values (the value closest
       * to negative infinity). Special values are handled in the following ways:
       * <ul>
       * <li>If either value is NaN, the result is NaN</li>
       * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li>
       * </ul>
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       * @return The smaller of the two specified half-precision values
       */
      public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
          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;
      }
  
      /**
       * Returns the larger of two half-precision float values (the value closest
       * to positive infinity). Special values are handled in the following ways:
       * <ul>
       * <li>If either value is NaN, the result is NaN</li>
       * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li>
       * </ul>
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return The larger of the two specified half-precision values
       */
      public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
          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;
      }
  
      /**
       * Returns true if the first half-precision float value is less (smaller
       * toward negative infinity) than the second half-precision float value.
       * If either of the values is NaN, the result is false.
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return True if x is less than y, false otherwise
       */
      public static boolean less(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
          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);
      }
  
      /**
       * Returns true if the first half-precision float value is less (smaller
       * toward negative infinity) than or equal to the second half-precision
       * float value. If either of the values is NaN, the result is false.
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return True if x is less than or equal to y, false otherwise
       */
      public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
          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);
      }
  
      /**
       * Returns true if the first half-precision float value is greater (larger
       * toward positive infinity) than the second half-precision float value.
       * If either of the values is NaN, the result is false.
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return True if x is greater than y, false otherwise
       */
      public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
          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);
      }
  
      /**
       * Returns true if the first half-precision float value is greater (larger
       * toward positive infinity) than or equal to the second half-precision float
       * value. If either of the values is NaN, the result is false.
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return True if x is greater than y, false otherwise
       */
      public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
          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);
      }
  
      /**
       * Returns true if the two half-precision float values are equal.
       * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO}
       * and {@link #NEGATIVE_ZERO} are considered equal.
       *
       * @param x The first half-precision value
       * @param y The second half-precision value
       *
       * @return True if x is equal to y, false otherwise
       */
      public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
          if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
          if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
  
          return x == y || ((x | y) & FP16_COMBINED) == 0;
      }
  
      /**
       * Returns the sign of the specified half-precision float.
       *
       * @param h A half-precision float value
       * @return 1 if the value is positive, -1 if the value is negative
       */
      public static int getSign(@HalfFloat short h) {
          return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
      }
  
      /**
       * Returns the unbiased exponent used in the representation of
       * the specified  half-precision float value. if the value is NaN
       * or infinite, this* method returns {@link #MAX_EXPONENT} + 1.
       * If the argument is 0 or a subnormal representation, this method
       * returns {@link #MIN_EXPONENT} - 1.
       *
       * @param h A half-precision float value
       * @return The unbiased exponent of the specified value
       */
      public static int getExponent(@HalfFloat short h) {
          return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
      }
  
      /**
       * Returns the significand, or mantissa, used in the representation
       * of the specified half-precision float value.
       *
       * @param h A half-precision float value
       * @return The significand, or significand, of the specified vlaue
       */
      public static int getSignificand(@HalfFloat short h) {
          return h & FP16_SIGNIFICAND_MASK;
      }
  
      /**
       * Returns true if the specified half-precision float value represents
       * infinity, false otherwise.
       *
       * @param h A half-precision float value
       * @return True if the value is positive infinity or negative infinity,
       *         false otherwise
       */
      public static boolean isInfinite(@HalfFloat short h) {
          return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
      }
  
      /**
       * Returns true if the specified half-precision float value represents
       * a Not-a-Number, false otherwise.
       *
       * @param h A half-precision float value
       * @return True if the value is a NaN, false otherwise
       */
      public static boolean isNaN(@HalfFloat short h) {
          return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
      }
  
      /**
       * Returns true if the specified half-precision float value is normalized
       * (does not have a subnormal representation). If the specified value is
       * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY},
       * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal
       * number, this method returns false.
       *
       * @param h A half-precision float value
       * @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;
      }
  
      /**
       * <p>Converts the specified half-precision float value into a
       * single-precision float value. The following special cases are handled:</p>
       * <ul>
       * <li>If the input is {@link #NaN}, the returned value is {@link Float#NaN}</li>
       * <li>If the input is {@link #POSITIVE_INFINITY} or
       * {@link #NEGATIVE_INFINITY}, the returned value is respectively
       * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li>
       * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li>
       * <li>Otherwise, the returned value is a normalized single-precision float value</li>
       * </ul>
       *
       * @param h The half-precision float value to convert to single-precision
       * @return A normalized single-precision float value
       */
      public static float toFloat(@HalfFloat short h) {
          int bits = h & 0xffff;
          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);
      }
  
      /**
       * <p>Converts the specified single-precision float value into a
       * half-precision float value. The following special cases are handled:</p>
       * <ul>
       * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned
       * value is {@link #NaN}</li>
       * <li>If the input is {@link Float#POSITIVE_INFINITY} or
       * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively
       * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li>
       * <li>If the input is 0 (positive or negative), the returned value is
       * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
       * <li>If the input is a less than {@link #MIN_VALUE}, the returned value
       * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
       * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value
       * is a denorm half-precision float</li>
       * <li>Otherwise, the returned value is rounded to the nearest
       * representable half-precision float value</li>
       * </ul>
       *
       * @param f The single-precision float value to convert to half-precision
       * @return A half-precision float value
       */
      @SuppressWarnings("StatementWithEmptyBody")
      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;
          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);
      }
  
      /**
       * 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
       * <code>toHalf(Float.parseString(h))</code>. 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
       * <code>toHalf(Float.parseString(h))</code>. 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(Float.parseFloat(s));
      }
  
      /**
       * Returns a string representation of the specified half-precision
       * float value. Calling this method is equivalent to calling
       * <code>Float.toString(toFloat(h))</code>. See {@link Float#toString(float)}
       * for more information on the format of the string representation.
       *
       * @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));
      }
  
      /**
       * <p>Returns a hexadecimal string representation of the specified half-precision
       * float value. If the value is a NaN, the result is <code>"NaN"</code>,
       * otherwise the result follows this format:</p>
       * <ul>
       * <li>If the sign is positive, no sign character appears in the result</li>
       * <li>If the sign is negative, the first character is <code>'-'</code></li>
       * <li>If the value is inifinity, the string is <code>"Infinity"</code></li>
       * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li>
       * <li>If the value has a normalized representation, the exponent and
       * significand are represented in the string in two fields. The significand
       * starts with <code>"0x1."</code> followed by its lowercase hexadecimal
       * representation. Trailing zeroes are removed unless all digits are 0, then
       * a single zero is used. The significand representation is followed by the
       * exponent, represented by <code>"p"</code>, itself followed by a decimal
       * string of the unbiased exponent</li>
       * <li>If the value has a subnormal representation, the significand starts
       * with <code>"0x0."</code> followed by its lowercase hexadecimal
       * representation. Trailing zeroes are removed unless all digits are 0, then
       * a single zero is used. The significand representation is followed by the
       * exponent, represented by <code>"p-14"</code></li>
       * </ul>
       *
       * @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();
  
          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();
      }
  }
  