glsl: Add "built-in" functions to do fp64_to_fp32(fp64)
authorElie Tournier <tournier.elie@gmail.com>
Wed, 9 Aug 2017 21:36:28 +0000 (22:36 +0100)
committerMatt Turner <mattst88@gmail.com>
Thu, 10 Jan 2019 00:42:40 +0000 (16:42 -0800)
Signed-off-by: Elie Tournier <elie.tournier@collabora.com>
src/compiler/glsl/float64.glsl

index ee87632afe3621e21b75ac5ee69822ab6dd23ff4..8cc0a2789042ba59da5770145b8d437ab040e36a 100644 (file)
@@ -944,3 +944,104 @@ __int_to_fp64(int a)
    }
    return __packFloat64(zSign, 0x412 - shiftCount, zFrac0, zFrac1);
 }
+
+/* Packs the sign `zSign', exponent `zExp', and significand `zFrac' into a
+ * single-precision floating-point value, returning the result.  After being
+ * shifted into the proper positions, the three fields are simply added
+ * together to form the result.  This means that any integer portion of `zSig'
+ * will be added into the exponent.  Since a properly normalized significand
+ * will have an integer portion equal to 1, the `zExp' input should be 1 less
+ * than the desired result exponent whenever `zFrac' is a complete, normalized
+ * significand.
+ */
+float
+__packFloat32(uint zSign, int zExp, uint zFrac)
+{
+   return uintBitsToFloat((zSign<<31) + (uint(zExp)<<23) + zFrac);
+}
+
+/* Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+ * and significand `zFrac', and returns the proper single-precision floating-
+ * point value corresponding to the abstract input.  Ordinarily, the abstract
+ * value is simply rounded and packed into the single-precision format, with
+ * the inexact exception raised if the abstract input cannot be represented
+ * exactly.  However, if the abstract value is too large, the overflow and
+ * inexact exceptions are raised and an infinity or maximal finite value is
+ * returned.  If the abstract value is too small, the input value is rounded to
+ * a subnormal number, and the underflow and inexact exceptions are raised if
+ * the abstract input cannot be represented exactly as a subnormal single-
+ * precision floating-point number.
+ *     The input significand `zFrac' has its binary point between bits 30
+ * and 29, which is 7 bits to the left of the usual location.  This shifted
+ * significand must be normalized or smaller.  If `zFrac' is not normalized,
+ * `zExp' must be 0; in that case, the result returned is a subnormal number,
+ * and it must not require rounding.  In the usual case that `zFrac' is
+ * normalized, `zExp' must be 1 less than the "true" floating-point exponent.
+ * The handling of underflow and overflow follows the IEEE Standard for
+ * Floating-Point Arithmetic.
+ */
+float
+__roundAndPackFloat32(uint zSign, int zExp, uint zFrac)
+{
+   bool roundNearestEven;
+   int roundIncrement;
+   int roundBits;
+
+   roundNearestEven = FLOAT_ROUNDING_MODE == FLOAT_ROUND_NEAREST_EVEN;
+   roundIncrement = 0x40;
+   if (!roundNearestEven) {
+      if (FLOAT_ROUNDING_MODE == FLOAT_ROUND_TO_ZERO) {
+         roundIncrement = 0;
+      } else {
+         roundIncrement = 0x7F;
+         if (zSign != 0u) {
+            if (FLOAT_ROUNDING_MODE == FLOAT_ROUND_UP)
+               roundIncrement = 0;
+         } else {
+            if (FLOAT_ROUNDING_MODE == FLOAT_ROUND_DOWN)
+               roundIncrement = 0;
+         }
+      }
+   }
+   roundBits = int(zFrac & 0x7Fu);
+   if (0xFDu <= uint(zExp)) {
+      if ((0xFD < zExp) || ((zExp == 0xFD) && (int(zFrac) + roundIncrement) < 0))
+         return __packFloat32(zSign, 0xFF, 0u) - float(roundIncrement == 0);
+      int count = -zExp;
+      bool zexp_lt0 = zExp < 0;
+      uint zFrac_lt0 = mix(uint(zFrac != 0u), (zFrac>>count) | uint((zFrac<<((-count) & 31)) != 0u), (-zExp) < 32);
+      zFrac = mix(zFrac, zFrac_lt0, zexp_lt0);
+      roundBits = mix(roundBits, int(zFrac) & 0x7f, zexp_lt0);
+      zExp = mix(zExp, 0, zexp_lt0);
+   }
+   zFrac = (zFrac + uint(roundIncrement))>>7;
+   zFrac &= ~uint(((roundBits ^ 0x40) == 0) && roundNearestEven);
+
+   return __packFloat32(zSign, mix(zExp, 0, zFrac == 0u), zFrac);
+}
+
+/* Returns the result of converting the double-precision floating-point value
+ * `a' to the single-precision floating-point format.  The conversion is
+ * performed according to the IEEE Standard for Floating-Point Arithmetic.
+ */
+float
+__fp64_to_fp32(uint64_t __a)
+{
+   uvec2 a = unpackUint2x32(__a);
+   uint zFrac = 0u;
+   uint allZero = 0u;
+
+   uint aFracLo = __extractFloat64FracLo(__a);
+   uint aFracHi = __extractFloat64FracHi(__a);
+   int aExp = __extractFloat64Exp(__a);
+   uint aSign = __extractFloat64Sign(__a);
+   if (aExp == 0x7FF) {
+      __shortShift64Left(a.y, a.x, 12, a.y, a.x);
+      float rval = uintBitsToFloat((aSign<<31) | 0x7FC00000u | (a.y>>9));
+      rval = mix(__packFloat32(aSign, 0xFF, 0u), rval, (aFracHi | aFracLo) != 0u);
+      return rval;
+   }
+   __shift64RightJamming(aFracHi, aFracLo, 22, allZero, zFrac);
+   zFrac = mix(zFrac, zFrac | 0x40000000u, aExp != 0);
+   return __roundAndPackFloat32(aSign, aExp - 0x381, zFrac);
+}