diff --git a/src/compiler/glsl/float64.glsl b/src/compiler/glsl/float64.glsl index ee87632afe3..8cc0a278904 100644 --- a/src/compiler/glsl/float64.glsl +++ b/src/compiler/glsl/float64.glsl @@ -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); +}