Files
d4r/tools/windows/binary16.h
T
xdfnx-devandrealdody 98de8e3a3e Build native Windows packages for both RDNA4 targets
Add explicit gfx1200/gfx1201 builds, runtime selection and per-target
code objects. Guard staging, packaging and installation against mixed
architectures; retain gfx1201 compatibility and strict K/M arithmetic.

Adapt realdody's target configuration and MSVC binary16/BOOL portability
fixes from ae530b0. Do not import its forced gfx12 layout on RDNA3.
Add exhaustive binary16 and C++/PowerShell target contract regressions.

Validation: 20 CTest gates on RX 9070 XT; four software-only gfx1200 gates;
MSVC portability compile checks; exact 4K K async and M control outputs;
isolated package install/restore and mixed-target rejection tests.
No physical gfx1200 validation is claimed.

Co-authored-by: realdody <[email protected]>
2026-10-02 16:18:18 +03:00

52 lines
2.6 KiB
C++

#pragma once
// Adapted from realdody/d4r ae530b0 (MIT), realdody <[email protected]>.
#include <cstdint>
#include <cstring>
#include <cmath>
#include <limits>
namespace d4r::diag::binary16 {
// MSVC has no _Float16 extension. Convert double<->IEEE binary16 explicitly with
// round-to-nearest-even, matching a native _Float16 cast. This probe compares the
// f16-accumulator MMA against a double sum rounded once to half, so the rounding
// must match IEEE 754 binary16 exactly.
inline uint16_t half_bits(double value) {
uint64_t bits;
std::memcpy(&bits, &value, sizeof(bits));
const uint16_t sign = static_cast<uint16_t>((bits >> 63) << 15);
int32_t exponent = static_cast<int32_t>((bits >> 52) & 0x7ff);
uint64_t mantissa = bits & 0x000fffffffffffffULL;
if (exponent == 0x7ff) return static_cast<uint16_t>(sign | (mantissa ? 0x7e00 : 0x7c00));
if (exponent == 0) return sign; // double subnormal rounds to half zero
int32_t unbiased = exponent - 1023;
if (unbiased > 15) return static_cast<uint16_t>(sign | 0x7c00);
mantissa |= 0x0010000000000000ULL; // implicit leading one (53-bit significand)
const int shift = unbiased >= -14 ? 42 : 28 - unbiased;
uint64_t rounded = shift >= 64 ? 0 : (mantissa >> shift);
if (shift < 64) {
const uint64_t remainder = mantissa & ((1ULL << shift) - 1);
const uint64_t halfway = 1ULL << (shift - 1);
if (remainder > halfway || (remainder == halfway && (rounded & 1))) ++rounded;
}
if (unbiased >= -14) {
if (rounded >= 0x800) { // rounding overflowed into the exponent
rounded = 0x400;
if (++unbiased > 15) return static_cast<uint16_t>(sign | 0x7c00);
}
return static_cast<uint16_t>(sign | ((unbiased + 15) << 10) | static_cast<uint16_t>(rounded & 0x3ff));
}
if (rounded >= 0x400) return static_cast<uint16_t>(sign | 0x0400); // smallest normal half
return static_cast<uint16_t>(sign | static_cast<uint16_t>(rounded));
}
inline double half_value(uint16_t bits) {
const bool negative = (bits & 0x8000u) != 0;
const uint32_t exponent = (bits >> 10) & 0x1fu;
const uint32_t mantissa = bits & 0x3ffu;
double magnitude;
if (exponent == 0) magnitude = std::ldexp(static_cast<double>(mantissa), -24);
else if (exponent == 0x1fu) magnitude = mantissa ? std::numeric_limits<double>::quiet_NaN() : std::numeric_limits<double>::infinity();
else magnitude = std::ldexp(static_cast<double>(mantissa) / 1024.0 + 1.0, static_cast<int>(exponent) - 15);
return negative ? -magnitude : magnitude;
}
}