| #ifndef _TCUFLOAT_HPP |
| #define _TCUFLOAT_HPP |
| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Tester Core |
| * ---------------------------------------- |
| * |
| * Copyright 2014 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. |
| * |
| *//*! |
| * \file |
| * \brief Reconfigurable floating-point value template. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "tcuDefs.hpp" |
| |
| // For memcpy(). |
| #include <string.h> |
| |
| namespace tcu |
| { |
| |
| enum FloatFlags |
| { |
| FLOAT_HAS_SIGN = (1<<0), |
| FLOAT_SUPPORT_DENORM = (1<<1) |
| }; |
| |
| enum RoundingDirection |
| { |
| ROUND_TO_EVEN = 0, |
| ROUND_DOWNWARD, // Towards -Inf. |
| ROUND_UPWARD, // Towards +Inf. |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Floating-point format template |
| * |
| * This template implements arbitrary floating-point handling. Template |
| * can be used for conversion between different formats and checking |
| * various properties of floating-point values. |
| *//*--------------------------------------------------------------------*/ |
| template <typename StorageType_, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| class Float |
| { |
| public: |
| typedef StorageType_ StorageType; |
| |
| enum |
| { |
| EXPONENT_BITS = ExponentBits, |
| MANTISSA_BITS = MantissaBits, |
| EXPONENT_BIAS = ExponentBias, |
| FLAGS = Flags, |
| }; |
| |
| Float (void); |
| explicit Float (StorageType value); |
| explicit Float (float v, RoundingDirection rd = ROUND_TO_EVEN); |
| explicit Float (double v, RoundingDirection rd = ROUND_TO_EVEN); |
| |
| template <typename OtherStorageType, int OtherExponentBits, int OtherMantissaBits, int OtherExponentBias, deUint32 OtherFlags> |
| static Float convert (const Float<OtherStorageType, OtherExponentBits, OtherMantissaBits, OtherExponentBias, OtherFlags>& src, RoundingDirection rd = ROUND_TO_EVEN); |
| |
| static inline Float convert (const Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>& src, RoundingDirection = ROUND_TO_EVEN) { return src; } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Construct floating point value |
| * \param sign Sign. Must be +1/-1 |
| * \param exponent Exponent in range [1-ExponentBias, ExponentBias+1] |
| * \param mantissa Mantissa bits with implicit leading bit explicitly set |
| * \return The specified float |
| * |
| * This function constructs a floating point value from its inputs. |
| * The normally implicit leading bit of the mantissa must be explicitly set. |
| * The exponent normally used for zero/subnormals is an invalid input. Such |
| * values are specified with the leading mantissa bit of zero and the lowest |
| * normal exponent (1-ExponentBias). Additionally having both exponent and |
| * mantissa set to zero is a shorthand notation for the correctly signed |
| * floating point zero. Inf and NaN must be specified directly with an |
| * exponent of ExponentBias+1 and the appropriate mantissa (with leading |
| * bit set) |
| *//*--------------------------------------------------------------------*/ |
| static inline Float construct (int sign, int exponent, StorageType mantissa); |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Construct floating point value. Explicit version |
| * \param sign Sign. Must be +1/-1 |
| * \param exponent Exponent in range [-ExponentBias, ExponentBias+1] |
| * \param mantissa Mantissa bits |
| * \return The specified float |
| * |
| * This function constructs a floating point value from its inputs with |
| * minimal intervention. |
| * The sign is turned into a sign bit and the exponent bias is added. |
| * See IEEE-754 for additional information on the inputs and |
| * the encoding of special values. |
| *//*--------------------------------------------------------------------*/ |
| static Float constructBits (int sign, int exponent, StorageType mantissaBits); |
| |
| StorageType bits (void) const { return m_value; } |
| float asFloat (void) const; |
| double asDouble (void) const; |
| |
| inline int signBit (void) const { return (int)(m_value >> (ExponentBits+MantissaBits)) & 1; } |
| inline StorageType exponentBits (void) const { return (m_value >> MantissaBits) & ((StorageType(1)<<ExponentBits)-1); } |
| inline StorageType mantissaBits (void) const { return m_value & ((StorageType(1)<<MantissaBits)-1); } |
| |
| inline int sign (void) const { return signBit() ? -1 : 1; } |
| inline int exponent (void) const { return isDenorm() ? 1 - ExponentBias : (int)exponentBits() - ExponentBias; } |
| inline StorageType mantissa (void) const { return isZero() || isDenorm() ? mantissaBits() : (mantissaBits() | (StorageType(1)<<MantissaBits)); } |
| |
| inline bool isInf (void) const { return exponentBits() == ((1<<ExponentBits)-1) && mantissaBits() == 0; } |
| inline bool isNaN (void) const { return exponentBits() == ((1<<ExponentBits)-1) && mantissaBits() != 0; } |
| inline bool isZero (void) const { return exponentBits() == 0 && mantissaBits() == 0; } |
| inline bool isDenorm (void) const { return exponentBits() == 0 && mantissaBits() != 0; } |
| |
| inline bool operator< (const Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>& other) const { return this->asDouble() < other.asDouble(); } |
| |
| static Float zero (int sign); |
| static Float inf (int sign); |
| static Float nan (void); |
| |
| static Float largestNormal (int sign); |
| static Float smallestNormal (int sign); |
| |
| private: |
| StorageType m_value; |
| } DE_WARN_UNUSED_TYPE; |
| |
| // Common floating-point types. |
| typedef Float<deUint16, 5, 10, 15, FLOAT_HAS_SIGN|FLOAT_SUPPORT_DENORM> Float16; //!< IEEE 754-2008 16-bit floating-point value |
| typedef Float<deUint32, 8, 23, 127, FLOAT_HAS_SIGN|FLOAT_SUPPORT_DENORM> Float32; //!< IEEE 754 32-bit floating-point value |
| typedef Float<deUint64, 11, 52, 1023, FLOAT_HAS_SIGN|FLOAT_SUPPORT_DENORM> Float64; //!< IEEE 754 64-bit floating-point value |
| |
| typedef Float<deUint16, 5, 10, 15, FLOAT_HAS_SIGN> Float16Denormless; //!< IEEE 754-2008 16-bit floating-point value without denormalized support |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::Float (void) |
| : m_value(0) |
| { |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::Float (StorageType value) |
| : m_value(value) |
| { |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::Float (float value, RoundingDirection rd) |
| : m_value(0) |
| { |
| deUint32 u32; |
| memcpy(&u32, &value, sizeof(deUint32)); |
| *this = convert(Float32(u32), rd); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::Float (double value, RoundingDirection rd) |
| : m_value(0) |
| { |
| deUint64 u64; |
| memcpy(&u64, &value, sizeof(deUint64)); |
| *this = convert(Float64(u64), rd); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline float Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::asFloat (void) const |
| { |
| float v; |
| deUint32 u32 = Float32::convert(*this).bits(); |
| memcpy(&v, &u32, sizeof(deUint32)); |
| return v; |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline double Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::asDouble (void) const |
| { |
| double v; |
| deUint64 u64 = Float64::convert(*this).bits(); |
| memcpy(&v, &u64, sizeof(deUint64)); |
| return v; |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::zero (int sign) |
| { |
| DE_ASSERT(sign == 1 || ((Flags & FLOAT_HAS_SIGN) && sign == -1)); |
| return Float(StorageType((sign > 0 ? 0ull : 1ull) << (ExponentBits+MantissaBits))); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::inf (int sign) |
| { |
| DE_ASSERT(sign == 1 || ((Flags & FLOAT_HAS_SIGN) && sign == -1)); |
| return Float(StorageType(((sign > 0 ? 0ull : 1ull) << (ExponentBits+MantissaBits)) | (((1ull<<ExponentBits)-1) << MantissaBits))); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::nan (void) |
| { |
| return Float(StorageType((1ull<<(ExponentBits+MantissaBits))-1)); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::largestNormal (int sign) |
| { |
| DE_ASSERT(sign == 1 || ((Flags & FLOAT_HAS_SIGN) && sign == -1)); |
| return Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::construct(sign, ExponentBias, (static_cast<StorageType>(1) << (MantissaBits + 1)) - 1); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::smallestNormal (int sign) |
| { |
| DE_ASSERT(sign == 1 || ((Flags & FLOAT_HAS_SIGN) && sign == -1)); |
| return Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::construct(sign, 1 - ExponentBias, (static_cast<StorageType>(1) << MantissaBits)); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::construct |
| (int sign, int exponent, StorageType mantissa) |
| { |
| // Repurpose this otherwise invalid input as a shorthand notation for zero (no need for caller to care about internal representation) |
| const bool isShorthandZero = exponent == 0 && mantissa == 0; |
| |
| // Handles the typical notation for zero (min exponent, mantissa 0). Note that the exponent usually used exponent (-ExponentBias) for zero/subnormals is not used. |
| // Instead zero/subnormals have the (normally implicit) leading mantissa bit set to zero. |
| const bool isDenormOrZero = (exponent == 1 - ExponentBias) && (mantissa >> MantissaBits == 0); |
| const StorageType s = StorageType((StorageType(sign < 0 ? 1 : 0)) << (StorageType(ExponentBits+MantissaBits))); |
| const StorageType exp = (isShorthandZero || isDenormOrZero) ? StorageType(0) : StorageType(exponent + ExponentBias); |
| |
| DE_ASSERT(sign == +1 || sign == -1); |
| DE_ASSERT(isShorthandZero || isDenormOrZero || mantissa >> MantissaBits == 1); |
| DE_ASSERT(exp >> ExponentBits == 0); |
| |
| return Float(StorageType(s | (exp << MantissaBits) | (mantissa & ((StorageType(1)<<MantissaBits)-1)))); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::constructBits |
| (int sign, int exponent, StorageType mantissaBits) |
| { |
| const StorageType signBit = static_cast<StorageType>(sign < 0 ? 1 : 0); |
| const StorageType exponentBits = static_cast<StorageType>(exponent + ExponentBias); |
| |
| DE_ASSERT(sign == +1 || sign == -1 ); |
| DE_ASSERT(exponentBits >> ExponentBits == 0); |
| DE_ASSERT(mantissaBits >> MantissaBits == 0); |
| |
| return Float(StorageType((signBit << (ExponentBits+MantissaBits)) | (exponentBits << MantissaBits) | (mantissaBits))); |
| } |
| |
| template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags> |
| template <typename OtherStorageType, int OtherExponentBits, int OtherMantissaBits, int OtherExponentBias, deUint32 OtherFlags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags> |
| Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::convert |
| (const Float<OtherStorageType, OtherExponentBits, OtherMantissaBits, OtherExponentBias, OtherFlags>& other, RoundingDirection rd) |
| { |
| if (!(Flags & FLOAT_HAS_SIGN) && other.sign() < 0) |
| { |
| // Negative number, truncate to zero. |
| return zero(+1); |
| } |
| |
| if (other.isInf()) |
| { |
| return inf(other.sign()); |
| } |
| |
| if (other.isNaN()) |
| { |
| return nan(); |
| } |
| |
| if (other.isZero()) |
| { |
| return zero(other.sign()); |
| } |
| |
| const int eMin = 1 - ExponentBias; |
| const int eMax = ((1<<ExponentBits)-2) - ExponentBias; |
| |
| const StorageType s = StorageType((StorageType(other.signBit())) << (StorageType(ExponentBits+MantissaBits))); // \note Not sign, but sign bit. |
| int e = other.exponent(); |
| deUint64 m = other.mantissa(); |
| |
| // Normalize denormalized values prior to conversion. |
| while (!(m & (1ull<<OtherMantissaBits))) |
| { |
| m <<= 1; |
| e -= 1; |
| } |
| |
| if (e < eMin) |
| { |
| // Underflow. |
| if ((Flags & FLOAT_SUPPORT_DENORM) && (eMin-e-1 <= MantissaBits)) |
| { |
| // Shift and round. |
| int bitDiff = (OtherMantissaBits-MantissaBits) + (eMin-e); |
| deUint64 lastBitsMask = (1ull << bitDiff) - 1ull; |
| deUint64 lastBits = (static_cast<deUint64>(m) & lastBitsMask); |
| deUint64 half = (1ull << (bitDiff - 1)) - 1; |
| deUint64 bias = (m >> bitDiff) & 1; |
| |
| switch (rd) |
| { |
| case ROUND_TO_EVEN: |
| return Float(StorageType(s | (m + half + bias) >> bitDiff)); |
| |
| case ROUND_DOWNWARD: |
| m = (m >> bitDiff); |
| if (lastBits != 0ull && other.sign() < 0) |
| { |
| m += 1; |
| } |
| return Float(StorageType(s | m)); |
| |
| case ROUND_UPWARD: |
| m = (m >> bitDiff); |
| if (lastBits != 0ull && other.sign() > 0) |
| { |
| m += 1; |
| } |
| return Float(StorageType(s | m)); |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| |
| return zero(other.sign()); |
| } |
| |
| // Remove leading 1. |
| m = m & ~(1ull<<OtherMantissaBits); |
| |
| if (MantissaBits < OtherMantissaBits) |
| { |
| // Round mantissa. |
| int bitDiff = OtherMantissaBits-MantissaBits; |
| deUint64 lastBitsMask = (1ull << bitDiff) - 1ull; |
| deUint64 lastBits = (static_cast<deUint64>(m) & lastBitsMask); |
| deUint64 half = (1ull << (bitDiff - 1)) - 1; |
| deUint64 bias = (m >> bitDiff) & 1; |
| |
| switch (rd) |
| { |
| case ROUND_TO_EVEN: |
| m = (m + half + bias) >> bitDiff; |
| break; |
| |
| case ROUND_DOWNWARD: |
| m = (m >> bitDiff); |
| if (lastBits != 0ull && other.sign() < 0) |
| { |
| m += 1; |
| } |
| break; |
| |
| case ROUND_UPWARD: |
| m = (m >> bitDiff); |
| if (lastBits != 0ull && other.sign() > 0) |
| { |
| m += 1; |
| } |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| if (m & (1ull<<MantissaBits)) |
| { |
| // Overflow in mantissa. |
| m = 0; |
| e += 1; |
| } |
| } |
| else |
| { |
| int bitDiff = MantissaBits-OtherMantissaBits; |
| m = m << bitDiff; |
| } |
| |
| if (e > eMax) |
| { |
| // Overflow. |
| return (((other.sign() < 0 && rd == ROUND_UPWARD) || (other.sign() > 0 && rd == ROUND_DOWNWARD)) ? largestNormal(other.sign()) : inf(other.sign())); |
| } |
| |
| DE_ASSERT(de::inRange(e, eMin, eMax)); |
| DE_ASSERT(((e + ExponentBias) & ~((1ull<<ExponentBits)-1)) == 0); |
| DE_ASSERT((m & ~((1ull<<MantissaBits)-1)) == 0); |
| |
| return Float(StorageType(s | (StorageType(e + ExponentBias) << MantissaBits) | m)); |
| } |
| |
| } // tcu |
| |
| #endif // _TCUFLOAT_HPP |