public float ReadHalf() { var encoded = ReadUInt16(); // Decode half float // Based on numpy implementation (https://github.com/numpy/numpy/blob/984bc91367f9b525eadef14c48c759999bc4adfc/numpy/core/src/npymath/halffloat.c#L477) uint decoded; uint halfExponent = (encoded & 0x7c00u); uint singleSign = (encoded & 0x8000u) << 16; switch (halfExponent) { case 0x0000u: /* 0 or subnormal */ uint halfSign = (encoded & 0x03ffu); if (halfSign == 0) { /* Signed zero */ decoded = singleSign; } else { /* Subnormal */ halfSign <<= 1; while ((halfSign & 0x0400u) == 0) { halfSign <<= 1; halfExponent++; } uint singleExponent = 127 - 15 - halfExponent << 23; uint singleSig = (halfSign & 0x03ffu) << 13; decoded = singleSign + singleExponent + singleSig; } break; case 0x7c00u: /* inf or NaN */ /* All-ones exponent and a copy of the significand */ decoded = singleSign + 0x7f800000u + ((encoded & 0x03ffu) << 13); break; default: /* normalized */ /* Just need to adjust the exponent and shift */ decoded = singleSign + (((encoded & 0x7fffu) + 0x1c000u) << 13); break; } return(UnsafeUtilities.ReinterpretCast <uint, float>(decoded)); }
// https://blog.fpmurphy.com/2008/12/half-precision-floating-point-format_14.html private static ushort FloatToHalf(float value) { int i = UnsafeUtilities.ReinterpretCast <float, int>(value); int s = (i >> 16) & 0x00008000; // sign int e = ((i >> 23) & 0x000000ff) - (127 - 15); // exponent int f = i & 0x007fffff; // fraction // need to handle NaNs and Inf? if (e <= 0) { if (e < -10) { if (s > 0) // handle -0.0 { return(0x8000); } else { return(0); } } f = (f | 0x00800000) >> (1 - e); return(( ushort )(s | (f >> 13))); } else if (e == 0xff - (127 - 15)) { if (f == 0) // Inf { return(( ushort )(s | 0x7c00)); } else { // NAN f >>= 13; return(( ushort )(s | 0x7c00 | f | (f == 0 ? 1 : 0))); } } else { if (e > 30) // Overflow { return(( ushort )(s | 0x7c00)); } return(( ushort )(s | (e << 10) | (f >> 13))); } }