| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2019 The Qt Company Ltd. | 
| 4 | ** Copyright (C) 2016 by Southwest Research Institute (R) | 
| 5 | ** Contact: http://www.qt-project.org/legal | 
| 6 | ** | 
| 7 | ** This file is part of the QtCore module of the Qt Toolkit. | 
| 8 | ** | 
| 9 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 10 | ** Commercial License Usage | 
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 12 | ** accordance with the commercial license agreement provided with the | 
| 13 | ** Software or, alternatively, in accordance with the terms contained in | 
| 14 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 16 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 17 | ** | 
| 18 | ** GNU Lesser General Public License Usage | 
| 19 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 20 | ** General Public License version 3 as published by the Free Software | 
| 21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 22 | ** packaging of this file. Please review the following information to | 
| 23 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 25 | ** | 
| 26 | ** GNU General Public License Usage | 
| 27 | ** Alternatively, this file may be used under the terms of the GNU | 
| 28 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 29 | ** Public license version 3 or any later version approved by the KDE Free | 
| 30 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 32 | ** included in the packaging of this file. Please review the following | 
| 33 | ** information to ensure the GNU General Public License requirements will | 
| 34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 35 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 36 | ** | 
| 37 | ** $QT_END_LICENSE$ | 
| 38 | ** | 
| 39 | ****************************************************************************/ | 
| 40 |  | 
| 41 | #ifndef QFLOAT16_H | 
| 42 | #define QFLOAT16_H | 
| 43 |  | 
| 44 | #include <QtCore/qglobal.h> | 
| 45 | #include <QtCore/qmetatype.h> | 
| 46 | #include <string.h> | 
| 47 |  | 
| 48 | #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__AVX2__) && !defined(__F16C__) | 
| 49 | // All processors that support AVX2 do support F16C too. That doesn't mean | 
| 50 | // we're allowed to use the intrinsics directly, so we'll do it only for | 
| 51 | // the Intel and Microsoft's compilers. | 
| 52 | #  if defined(Q_CC_INTEL) || defined(Q_CC_MSVC) | 
| 53 | #    define __F16C__        1 | 
| 54 | # endif | 
| 55 | #endif | 
| 56 |  | 
| 57 | #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__) | 
| 58 | #include <immintrin.h> | 
| 59 | #endif | 
| 60 |  | 
| 61 | QT_BEGIN_NAMESPACE | 
| 62 |  | 
| 63 | #if 0 | 
| 64 | #pragma qt_class(QFloat16) | 
| 65 | #pragma qt_no_master_include | 
| 66 | #endif | 
| 67 |  | 
| 68 | class qfloat16 | 
| 69 | { | 
| 70 |     struct Wrap | 
| 71 |     { | 
| 72 |         // To let our private constructor work, without other code seeing | 
| 73 |         // ambiguity when constructing from int, double &c. | 
| 74 |         quint16 b16; | 
| 75 |         constexpr inline explicit Wrap(int value) : b16(quint16(value)) {} | 
| 76 |     }; | 
| 77 | public: | 
| 78 |     constexpr inline qfloat16() noexcept : b16(0) {} | 
| 79 |     inline qfloat16(float f) noexcept; | 
| 80 |     inline operator float() const noexcept; | 
| 81 |  | 
| 82 |     // Support for qIs{Inf,NaN,Finite}: | 
| 83 |     bool isInf() const noexcept { return (b16 & 0x7fff) == 0x7c00; } | 
| 84 |     bool isNaN() const noexcept { return (b16 & 0x7fff) > 0x7c00; } | 
| 85 |     bool isFinite() const noexcept { return (b16 & 0x7fff) < 0x7c00; } | 
| 86 |     Q_CORE_EXPORT int fpClassify() const noexcept; | 
| 87 |     // Can't specialize std::copysign() for qfloat16 | 
| 88 |     qfloat16 copySign(qfloat16 sign) const noexcept | 
| 89 |     { return qfloat16(Wrap((sign.b16 & 0x8000) | (b16 & 0x7fff))); } | 
| 90 |     // Support for std::numeric_limits<qfloat16> | 
| 91 |     static constexpr qfloat16 _limit_epsilon()    noexcept { return qfloat16(Wrap(0x1400)); } | 
| 92 |     static constexpr qfloat16 _limit_min()        noexcept { return qfloat16(Wrap(0x400)); } | 
| 93 |     static constexpr qfloat16 _limit_denorm_min() noexcept { return qfloat16(Wrap(1)); } | 
| 94 |     static constexpr qfloat16 _limit_max()        noexcept { return qfloat16(Wrap(0x7bff)); } | 
| 95 |     static constexpr qfloat16 _limit_lowest()     noexcept { return qfloat16(Wrap(0xfbff)); } | 
| 96 |     static constexpr qfloat16 _limit_infinity()   noexcept { return qfloat16(Wrap(0x7c00)); } | 
| 97 |     static constexpr qfloat16 _limit_quiet_NaN()  noexcept { return qfloat16(Wrap(0x7e00)); } | 
| 98 | #if QT_CONFIG(signaling_nan) | 
| 99 |     static constexpr qfloat16 _limit_signaling_NaN() noexcept { return qfloat16(Wrap(0x7d00)); } | 
| 100 | #endif | 
| 101 |     inline constexpr bool isNormal() const noexcept | 
| 102 |     { return (b16 & 0x7c00) && (b16 & 0x7c00) != 0x7c00; } | 
| 103 | private: | 
| 104 |     quint16 b16; | 
| 105 |     constexpr inline explicit qfloat16(Wrap nibble) noexcept : b16(nibble.b16) {} | 
| 106 |  | 
| 107 |     Q_CORE_EXPORT static const quint32 mantissatable[]; | 
| 108 |     Q_CORE_EXPORT static const quint32 exponenttable[]; | 
| 109 |     Q_CORE_EXPORT static const quint32 offsettable[]; | 
| 110 |     Q_CORE_EXPORT static const quint16 basetable[]; | 
| 111 |     Q_CORE_EXPORT static const quint16 shifttable[]; | 
| 112 |     Q_CORE_EXPORT static const quint32 roundtable[]; | 
| 113 |  | 
| 114 |     friend bool qIsNull(qfloat16 f) noexcept; | 
| 115 | #if !defined(QT_NO_FLOAT16_OPERATORS) | 
| 116 |     friend qfloat16 operator-(qfloat16 a) noexcept; | 
| 117 | #endif | 
| 118 | }; | 
| 119 |  | 
| 120 | Q_DECLARE_TYPEINFO(qfloat16, Q_PRIMITIVE_TYPE); | 
| 121 |  | 
| 122 | Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *, const float *, qsizetype length) noexcept; | 
| 123 | Q_CORE_EXPORT void qFloatFromFloat16(float *, const qfloat16 *, qsizetype length) noexcept; | 
| 124 |  | 
| 125 | // Complement qnumeric.h: | 
| 126 | [[nodiscard]] inline bool qIsInf(qfloat16 f) noexcept { return f.isInf(); } | 
| 127 | [[nodiscard]] inline bool qIsNaN(qfloat16 f) noexcept { return f.isNaN(); } | 
| 128 | [[nodiscard]] inline bool qIsFinite(qfloat16 f) noexcept { return f.isFinite(); } | 
| 129 | [[nodiscard]] inline int qFpClassify(qfloat16 f) noexcept { return f.fpClassify(); } | 
| 130 | // [[nodiscard]] quint32 qFloatDistance(qfloat16 a, qfloat16 b); | 
| 131 |  | 
| 132 | // The remainder of these utility functions complement qglobal.h | 
| 133 | [[nodiscard]] inline int qRound(qfloat16 d) noexcept | 
| 134 | { return qRound(static_cast<float>(d)); } | 
| 135 |  | 
| 136 | [[nodiscard]] inline qint64 qRound64(qfloat16 d) noexcept | 
| 137 | { return qRound64(static_cast<float>(d)); } | 
| 138 |  | 
| 139 | [[nodiscard]] inline bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept | 
| 140 | { | 
| 141 |     float f1 = static_cast<float>(p1); | 
| 142 |     float f2 = static_cast<float>(p2); | 
| 143 |     // The significand precision for IEEE754 half precision is | 
| 144 |     // 11 bits (10 explicitly stored), or approximately 3 decimal | 
| 145 |     // digits.  In selecting the fuzzy comparison factor of 102.5f | 
| 146 |     // (that is, (2^10+1)/10) below, we effectively select a | 
| 147 |     // window of about 1 (least significant) decimal digit about | 
| 148 |     // which the two operands can vary and still return true. | 
| 149 |     return (qAbs(f1 - f2) * 102.5f <= qMin(qAbs(f1), qAbs(f2))); | 
| 150 | } | 
| 151 |  | 
| 152 | [[nodiscard]] inline bool qIsNull(qfloat16 f) noexcept | 
| 153 | { | 
| 154 |     return (f.b16 & static_cast<quint16>(0x7fff)) == 0; | 
| 155 | } | 
| 156 |  | 
| 157 | inline int qIntCast(qfloat16 f) noexcept | 
| 158 | { return int(static_cast<float>(f)); } | 
| 159 |  | 
| 160 | #ifndef Q_QDOC | 
| 161 | QT_WARNING_PUSH | 
| 162 | QT_WARNING_DISABLE_CLANG("-Wc99-extensions" ) | 
| 163 | QT_WARNING_DISABLE_GCC("-Wold-style-cast" ) | 
| 164 | inline qfloat16::qfloat16(float f) noexcept | 
| 165 | { | 
| 166 | #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__) | 
| 167 |     __m128 packsingle = _mm_set_ss(f); | 
| 168 |     __m128i packhalf = _mm_cvtps_ph(packsingle, 0); | 
| 169 |     b16 = _mm_extract_epi16(packhalf, 0); | 
| 170 | #elif defined (__ARM_FP16_FORMAT_IEEE) | 
| 171 |     __fp16 f16 = __fp16(f); | 
| 172 |     memcpy(&b16, &f16, sizeof(quint16)); | 
| 173 | #else | 
| 174 |     quint32 u; | 
| 175 |     memcpy(&u, &f, sizeof(quint32)); | 
| 176 |     const quint32 signAndExp = u >> 23; | 
| 177 |     const quint16 base = basetable[signAndExp]; | 
| 178 |     const quint16 shift = shifttable[signAndExp]; | 
| 179 |     const quint32 round = roundtable[signAndExp]; | 
| 180 |     quint32 mantissa = (u & 0x007fffff); | 
| 181 |     if ((signAndExp & 0xff) == 0xff) { | 
| 182 |         if (mantissa) // keep nan from truncating to inf | 
| 183 |             mantissa = qMax(1U << shift, mantissa); | 
| 184 |     } else { | 
| 185 |         // round half to even | 
| 186 |         mantissa += round; | 
| 187 |         if (mantissa & (1 << shift)) | 
| 188 |             --mantissa; | 
| 189 |     } | 
| 190 |  | 
| 191 |     // We use add as the mantissa may overflow causing | 
| 192 |     // the exp part to shift exactly one value. | 
| 193 |     b16 = quint16(base + (mantissa >> shift)); | 
| 194 | #endif | 
| 195 | } | 
| 196 | QT_WARNING_POP | 
| 197 |  | 
| 198 | inline qfloat16::operator float() const noexcept | 
| 199 | { | 
| 200 | #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__) | 
| 201 |     __m128i packhalf = _mm_cvtsi32_si128(b16); | 
| 202 |     __m128 packsingle = _mm_cvtph_ps(packhalf); | 
| 203 |     return _mm_cvtss_f32(packsingle); | 
| 204 | #elif defined (__ARM_FP16_FORMAT_IEEE) | 
| 205 |     __fp16 f16; | 
| 206 |     memcpy(&f16, &b16, sizeof(quint16)); | 
| 207 |     return float(f16); | 
| 208 | #else | 
| 209 |     quint32 u = mantissatable[offsettable[b16 >> 10] + (b16 & 0x3ff)] | 
| 210 |                 + exponenttable[b16 >> 10]; | 
| 211 |     float f; | 
| 212 |     memcpy(&f, &u, sizeof(quint32)); | 
| 213 |     return f; | 
| 214 | #endif | 
| 215 | } | 
| 216 | #endif | 
| 217 |  | 
| 218 | #if !defined(QT_NO_FLOAT16_OPERATORS) | 
| 219 | inline qfloat16 operator-(qfloat16 a) noexcept | 
| 220 | { | 
| 221 |     qfloat16 f; | 
| 222 |     f.b16 = a.b16 ^ quint16(0x8000); | 
| 223 |     return f; | 
| 224 | } | 
| 225 |  | 
| 226 | inline qfloat16 operator+(qfloat16 a, qfloat16 b) noexcept { return qfloat16(static_cast<float>(a) + static_cast<float>(b)); } | 
| 227 | inline qfloat16 operator-(qfloat16 a, qfloat16 b) noexcept { return qfloat16(static_cast<float>(a) - static_cast<float>(b)); } | 
| 228 | inline qfloat16 operator*(qfloat16 a, qfloat16 b) noexcept { return qfloat16(static_cast<float>(a) * static_cast<float>(b)); } | 
| 229 | inline qfloat16 operator/(qfloat16 a, qfloat16 b) noexcept { return qfloat16(static_cast<float>(a) / static_cast<float>(b)); } | 
| 230 |  | 
| 231 | #define QF16_MAKE_ARITH_OP_FP(FP, OP) \ | 
| 232 |     inline FP operator OP(qfloat16 lhs, FP rhs) noexcept { return static_cast<FP>(lhs) OP rhs; } \ | 
| 233 |     inline FP operator OP(FP lhs, qfloat16 rhs) noexcept { return lhs OP static_cast<FP>(rhs); } | 
| 234 | #define QF16_MAKE_ARITH_OP_EQ_FP(FP, OP_EQ, OP) \ | 
| 235 |     inline qfloat16& operator OP_EQ(qfloat16& lhs, FP rhs) noexcept \ | 
| 236 |     { lhs = qfloat16(float(static_cast<FP>(lhs) OP rhs)); return lhs; } | 
| 237 | #define QF16_MAKE_ARITH_OP(FP) \ | 
| 238 |     QF16_MAKE_ARITH_OP_FP(FP, +) \ | 
| 239 |     QF16_MAKE_ARITH_OP_FP(FP, -) \ | 
| 240 |     QF16_MAKE_ARITH_OP_FP(FP, *) \ | 
| 241 |     QF16_MAKE_ARITH_OP_FP(FP, /) \ | 
| 242 |     QF16_MAKE_ARITH_OP_EQ_FP(FP, +=, +) \ | 
| 243 |     QF16_MAKE_ARITH_OP_EQ_FP(FP, -=, -) \ | 
| 244 |     QF16_MAKE_ARITH_OP_EQ_FP(FP, *=, *) \ | 
| 245 |     QF16_MAKE_ARITH_OP_EQ_FP(FP, /=, /) | 
| 246 | QF16_MAKE_ARITH_OP(long double) | 
| 247 | QF16_MAKE_ARITH_OP(double) | 
| 248 | QF16_MAKE_ARITH_OP(float) | 
| 249 | #undef QF16_MAKE_ARITH_OP | 
| 250 | #undef QF16_MAKE_ARITH_OP_FP | 
| 251 |  | 
| 252 | #define QF16_MAKE_ARITH_OP_INT(OP) \ | 
| 253 |     inline double operator OP(qfloat16 lhs, int rhs) noexcept { return static_cast<double>(lhs) OP rhs; } \ | 
| 254 |     inline double operator OP(int lhs, qfloat16 rhs) noexcept { return lhs OP static_cast<double>(rhs); } | 
| 255 | QF16_MAKE_ARITH_OP_INT(+) | 
| 256 | QF16_MAKE_ARITH_OP_INT(-) | 
| 257 | QF16_MAKE_ARITH_OP_INT(*) | 
| 258 | QF16_MAKE_ARITH_OP_INT(/) | 
| 259 | #undef QF16_MAKE_ARITH_OP_INT | 
| 260 |  | 
| 261 | QT_WARNING_PUSH | 
| 262 | QT_WARNING_DISABLE_FLOAT_COMPARE | 
| 263 |  | 
| 264 | inline bool operator>(qfloat16 a, qfloat16 b)  noexcept { return static_cast<float>(a) >  static_cast<float>(b); } | 
| 265 | inline bool operator<(qfloat16 a, qfloat16 b)  noexcept { return static_cast<float>(a) <  static_cast<float>(b); } | 
| 266 | inline bool operator>=(qfloat16 a, qfloat16 b) noexcept { return static_cast<float>(a) >= static_cast<float>(b); } | 
| 267 | inline bool operator<=(qfloat16 a, qfloat16 b) noexcept { return static_cast<float>(a) <= static_cast<float>(b); } | 
| 268 | inline bool operator==(qfloat16 a, qfloat16 b) noexcept { return static_cast<float>(a) == static_cast<float>(b); } | 
| 269 | inline bool operator!=(qfloat16 a, qfloat16 b) noexcept { return static_cast<float>(a) != static_cast<float>(b); } | 
| 270 |  | 
| 271 | #define QF16_MAKE_BOOL_OP_FP(FP, OP) \ | 
| 272 |     inline bool operator OP(qfloat16 lhs, FP rhs) noexcept { return static_cast<FP>(lhs) OP rhs; } \ | 
| 273 |     inline bool operator OP(FP lhs, qfloat16 rhs) noexcept { return lhs OP static_cast<FP>(rhs); } | 
| 274 | #define QF16_MAKE_BOOL_OP(FP) \ | 
| 275 |     QF16_MAKE_BOOL_OP_FP(FP, <) \ | 
| 276 |     QF16_MAKE_BOOL_OP_FP(FP, >) \ | 
| 277 |     QF16_MAKE_BOOL_OP_FP(FP, >=) \ | 
| 278 |     QF16_MAKE_BOOL_OP_FP(FP, <=) \ | 
| 279 |     QF16_MAKE_BOOL_OP_FP(FP, ==) \ | 
| 280 |     QF16_MAKE_BOOL_OP_FP(FP, !=) | 
| 281 | QF16_MAKE_BOOL_OP(long double) | 
| 282 | QF16_MAKE_BOOL_OP(double) | 
| 283 | QF16_MAKE_BOOL_OP(float) | 
| 284 | #undef QF16_MAKE_BOOL_OP | 
| 285 | #undef QF16_MAKE_BOOL_OP_FP | 
| 286 |  | 
| 287 | #define QF16_MAKE_BOOL_OP_INT(OP) \ | 
| 288 |     inline bool operator OP(qfloat16 a, int b) noexcept { return static_cast<float>(a) OP static_cast<float>(b); } \ | 
| 289 |     inline bool operator OP(int a, qfloat16 b) noexcept { return static_cast<float>(a) OP static_cast<float>(b); } | 
| 290 | QF16_MAKE_BOOL_OP_INT(>) | 
| 291 | QF16_MAKE_BOOL_OP_INT(<) | 
| 292 | QF16_MAKE_BOOL_OP_INT(>=) | 
| 293 | QF16_MAKE_BOOL_OP_INT(<=) | 
| 294 | QF16_MAKE_BOOL_OP_INT(==) | 
| 295 | QF16_MAKE_BOOL_OP_INT(!=) | 
| 296 | #undef QF16_MAKE_BOOL_OP_INT | 
| 297 |  | 
| 298 | QT_WARNING_POP | 
| 299 | #endif // QT_NO_FLOAT16_OPERATORS | 
| 300 |  | 
| 301 | /*! | 
| 302 |   \internal | 
| 303 | */ | 
| 304 | [[nodiscard]] inline bool qFuzzyIsNull(qfloat16 f) noexcept | 
| 305 | { | 
| 306 |     return qAbs(static_cast<float>(f)) <= 0.001f; | 
| 307 | } | 
| 308 |  | 
| 309 | QT_END_NAMESPACE | 
| 310 |  | 
| 311 | Q_DECLARE_METATYPE(qfloat16) | 
| 312 |  | 
| 313 | namespace std { | 
| 314 | template<> | 
| 315 | class numeric_limits<QT_PREPEND_NAMESPACE(qfloat16)> : public numeric_limits<float> | 
| 316 | { | 
| 317 | public: | 
| 318 |     /* | 
| 319 |       Treat quint16 b16 as if it were: | 
| 320 |       uint S: 1; // b16 >> 15 (sign); can be set for zero | 
| 321 |       uint E: 5; // (b16 >> 10) & 0x1f (offset exponent) | 
| 322 |       uint M: 10; // b16 & 0x3ff (adjusted mantissa) | 
| 323 |  | 
| 324 |       for E == 0: magnitude is M / 2.^{24} | 
| 325 |       for 0 < E < 31: magnitude is (1. + M / 2.^{10}) * 2.^{E - 15) | 
| 326 |       for E == 31: not finite | 
| 327 |      */ | 
| 328 |     static constexpr int digits = 11; | 
| 329 |     static constexpr int min_exponent = -13; | 
| 330 |     static constexpr int max_exponent = 16; | 
| 331 |  | 
| 332 |     static constexpr int digits10 = 3; | 
| 333 |     static constexpr int max_digits10 = 5; | 
| 334 |     static constexpr int min_exponent10 = -4; | 
| 335 |     static constexpr int max_exponent10 = 4; | 
| 336 |  | 
| 337 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) epsilon() | 
| 338 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_epsilon(); } | 
| 339 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) (min)() | 
| 340 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_min(); } | 
| 341 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) denorm_min() | 
| 342 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_denorm_min(); } | 
| 343 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) (max)() | 
| 344 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_max(); } | 
| 345 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) lowest() | 
| 346 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_lowest(); } | 
| 347 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) infinity() | 
| 348 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_infinity(); } | 
| 349 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) quiet_NaN() | 
| 350 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_quiet_NaN(); } | 
| 351 | #if QT_CONFIG(signaling_nan) | 
| 352 |     static constexpr QT_PREPEND_NAMESPACE(qfloat16) signaling_NaN() | 
| 353 |     { return QT_PREPEND_NAMESPACE(qfloat16)::_limit_signaling_NaN(); } | 
| 354 | #else | 
| 355 |     static constexpr bool has_signaling_NaN = false; | 
| 356 | #endif | 
| 357 | }; | 
| 358 |  | 
| 359 | template<> class numeric_limits<const QT_PREPEND_NAMESPACE(qfloat16)> | 
| 360 |     : public numeric_limits<QT_PREPEND_NAMESPACE(qfloat16)> {}; | 
| 361 | template<> class numeric_limits<volatile QT_PREPEND_NAMESPACE(qfloat16)> | 
| 362 |     : public numeric_limits<QT_PREPEND_NAMESPACE(qfloat16)> {}; | 
| 363 | template<> class numeric_limits<const volatile QT_PREPEND_NAMESPACE(qfloat16)> | 
| 364 |     : public numeric_limits<QT_PREPEND_NAMESPACE(qfloat16)> {}; | 
| 365 |  | 
| 366 | // Adding overloads to std isn't allowed, so we can't extend this to support | 
| 367 | // for fpclassify(), isnormal() &c. (which, furthermore, are macros on MinGW). | 
| 368 | } // namespace std | 
| 369 |  | 
| 370 | #endif // QFLOAT16_H | 
| 371 |  |