| 342 | outExponent = OUT_TYPE(adjustedExponent) << OUT_FORMAT::EXPONENT_SHIFT; |
| 343 | outMantissa = OUT_TYPE(mantissa); |
| 344 | } |
| 345 | |
| 346 | return outSign | outExponent | outMantissa; |
| 347 | } |
| 348 | |
| 349 | template<typename IN_FORMAT, typename IN_TYPE> |
| 350 | float UpConvert(IN_TYPE const bits) |
| 351 | { |
| 352 | using OUT_FORMAT = HELPER<FLOAT32>; |
| 353 | |
| 354 | static_assert(OUT_FORMAT::MANTISSA_WIDTH > IN_FORMAT::MANTISSA_WIDTH); |
| 355 | static_assert(OUT_FORMAT::EXPONENT_WIDTH > IN_FORMAT::EXPONENT_WIDTH); |
| 356 | static_assert(OUT_FORMAT::EXPONENT_BIAS > IN_FORMAT::EXPONENT_BIAS + IN_FORMAT::MANTISSA_WIDTH); |
| 357 | static_assert(IN_FORMAT::TOTAL_WIDTH == sizeof(IN_TYPE) * 8); |
| 358 | |
| 359 | IN_TYPE const inSign = bits & IN_FORMAT::SIGN_BIT; |
| 360 | IN_TYPE const inExponent = bits & IN_FORMAT::EXPONENT_MASK; |
| 361 | IN_TYPE const inMantissa = bits & IN_FORMAT::MANTISSA_MASK; |
| 362 | |
| 363 | uint32_t const outSign = uint32_t(inSign) << (OUT_FORMAT::SIGN_BIT_SHIFT - IN_FORMAT::SIGN_BIT_SHIFT); |
| 364 | uint32_t outExponent = uint32_t(inExponent) << (OUT_FORMAT::EXPONENT_SHIFT - IN_FORMAT::EXPONENT_SHIFT); |
| 365 | outExponent += (OUT_FORMAT::EXPONENT_BIAS - IN_FORMAT::EXPONENT_BIAS) << OUT_FORMAT::EXPONENT_SHIFT; |
| 366 | uint32_t outMantissa = uint32_t(inMantissa) << (OUT_FORMAT::MANTISSA_WIDTH - IN_FORMAT::MANTISSA_WIDTH); |
| 367 | |
| 368 | if (inExponent == IN_FORMAT::EXPONENT_MASK && (IN_FORMAT::SUPPORTS_INF || inMantissa == IN_FORMAT::MANTISSA_MASK)) // Inf or NaN |
| 369 | { |
| 370 | outExponent = OUT_FORMAT::EXPONENT_MASK; |
| 371 | } |
| 372 | else if (inExponent == 0) // Denormal |
| 373 | { |
| 374 | if (inMantissa != 0) // Non-zero denormal - convert to a normal number, there must be enough exponent bits for that |
| 375 | { |
| 376 | // TODO: When Donut moves to C++20, replace this platform-specific |
| 377 | // leading-zero-count block with std::countl_zero (<bit>): |
| 378 | // https://en.cppreference.com/cpp/numeric/countl_zero |
| 379 | #ifdef _MSC_VER |
| 380 | #if defined(_M_ARM64) || defined(_M_ARM64EC) |
| 381 | // __lzcnt is x86-only; use _BitScanReverse on ARM64. |
| 382 | // inMantissa is guaranteed non-zero here (checked above). |
| 383 | unsigned long msbIndex; |
| 384 | _BitScanReverse(&msbIndex, uint32_t(inMantissa)); |
| 385 | uint32_t leadingZeros = 31u - uint32_t(msbIndex); |
| 386 | #else |
| 387 | uint32_t leadingZeros = __lzcnt(uint32_t(inMantissa)); |
| 388 | #endif |
| 389 | #else |
| 390 | uint32_t leadingZeros = __builtin_clz(uint32_t(inMantissa)); |
| 391 | #endif |
| 392 | // Don't count the bits from uint32_t higher than the mantissa |
| 393 | leadingZeros -= 32 - IN_FORMAT::MANTISSA_WIDTH; |
| 394 | |
| 395 | // Shift the mantissa so that its highest "one" bit becomes the hidden "one" |