lin
2025-08-14 dae8bad597b6607a449b32bf76c523423f7720ed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
#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)
};
 
/*--------------------------------------------------------------------*//*!
 * \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);
   explicit                Float            (double v);
 
   template <typename OtherStorageType, int OtherExponentBits, int OtherMantissaBits, int OtherExponentBias, deUint32 OtherFlags>
   static Float            convert            (const Float<OtherStorageType, OtherExponentBits, OtherMantissaBits, OtherExponentBias, OtherFlags>& src);
 
   static inline Float        convert            (const Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>& src) { 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;    }
 
   static Float            zero            (int sign);
   static Float            inf                (int sign);
   static Float            nan                (void);
 
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)
   : m_value(0)
{
   deUint32 u32;
   memcpy(&u32, &value, sizeof(deUint32));
   *this = convert(Float32(u32));
}
 
template <typename StorageType, int ExponentBits, int MantissaBits, int ExponentBias, deUint32 Flags>
inline Float<StorageType, ExponentBits, MantissaBits, ExponentBias, Flags>::Float (double value)
   : m_value(0)
{
   deUint64 u64;
   memcpy(&u64, &value, sizeof(deUint64));
   *this = convert(Float64(u64));
}
 
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>
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)
{
   if (!(Flags & FLOAT_HAS_SIGN) && other.sign() < 0)
   {
       // Negative number, truncate to zero.
       return zero(+1);
   }
   else if (other.isInf())
   {
       return inf(other.sign());
   }
   else if (other.isNaN())
   {
       return nan();
   }
   else if (other.isZero())
   {
       return zero(other.sign());
   }
   else
   {
       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 (RTE).
               int            bitDiff    = (OtherMantissaBits-MantissaBits) + (eMin-e);
               deUint64    half    = (1ull << (bitDiff - 1)) - 1;
               deUint64    bias    = (m >> bitDiff) & 1;
 
               return Float(StorageType(s | (m + half + bias) >> bitDiff));
           }
           else
               return zero(other.sign());
       }
       else
       {
           // Remove leading 1.
           m = m & ~(1ull<<OtherMantissaBits);
 
           if (MantissaBits < OtherMantissaBits)
           {
               // Round mantissa (round to nearest even).
               int            bitDiff    = OtherMantissaBits-MantissaBits;
               deUint64    half    = (1ull << (bitDiff - 1)) - 1;
               deUint64    bias    = (m >> bitDiff) & 1;
 
               m = (m + half + bias) >> bitDiff;
 
               if (m & (1ull<<MantissaBits))
               {
                   // Overflow in mantissa.
                   m  = 0;
                   e += 1;
               }
           }
           else
           {
               int bitDiff = MantissaBits-OtherMantissaBits;
               m = m << bitDiff;
           }
 
           if (e > eMax)
           {
               // Overflow.
               return inf(other.sign());
           }
           else
           {
               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