#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static Unity.Burst.Intrinsics.Arm.Neon; using static Unity.Burst.Intrinsics.X86.F16C; namespace Unity.Burst.Intrinsics { /// /// Represents a 16-bit floating point value (half precision) /// Warning: this type may not be natively supported by your hardware, or its usage may be suboptimal /// public readonly struct f16 : System.IEquatable { /// /// The container for the actual 16-bit half precision floating point value /// private readonly ushort value; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint f32tof16(float x) { if (IsF16CSupported) { var v = new v128(); v.Float0 = x; var result = cvtps_ph(v, (int)X86.RoundingMode.FROUND_TRUNC_NOEXC); return result.UShort0; } else if (IsNeonHalfFPSupported) { var v = new v128(); v.Float0 = x; var result = vcvt_f16_f32(v); return result.UShort0; } // Managed fallback const int infinity_32 = 255 << 23; const uint msk = 0x7FFFF000u; uint ux = asuint(x); uint uux = ux & msk; uint h = (uint)(asuint(min(asfloat(uux) * 1.92592994e-34f, 260042752.0f)) + 0x1000) >> 13; // Clamp to signed infinity if overflowed h = select(h, select(0x7c00u, 0x7e00u, (int)uux > infinity_32), (int)uux >= infinity_32); // NaN->qNaN and Inf->Inf return h | (ux & ~msk) >> 16; } /// Returns the bit pattern of a float as a uint. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint asuint(float x) { return (uint)asint(x); } /// Returns the minimum of two float values. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float min(float x, float y) { return float.IsNaN(y) || x < y ? x : y; } /// Returns b if c is true, a otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint select(uint a, uint b, bool c) { return c ? b : a; } /// Returns the bit pattern of a uint as a float. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float asfloat(uint x) { return asfloat((int)x); } [StructLayout(LayoutKind.Explicit)] private struct IntFloatUnion { [FieldOffset(0)] public int intValue; [FieldOffset(0)] public float floatValue; } /// Returns the bit pattern of an int as a float. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float asfloat(int x) { IntFloatUnion u; u.floatValue = 0; u.intValue = x; return u.floatValue; } /// Returns the bit pattern of a float as an int. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int asint(float x) { IntFloatUnion u; u.intValue = 0; u.floatValue = x; return u.intValue; } /// Constructs a half value from a half value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public f16(f16 x) { value = x.value; } /// Constructs a half value from a float value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public f16(float v) { value = (ushort)f32tof16(v); } /// Returns whether two f16 values are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(f16 lhs, f16 rhs) { return lhs.value == rhs.value; } /// Returns whether two f16 values are different. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(f16 lhs, f16 rhs) { return lhs.value != rhs.value; } /// Returns true if the f16 is equal to a given f16, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(f16 rhs) { return value == rhs.value; } /// Returns true if the half is equal to a given half, false otherwise. public override bool Equals(object o) { return Equals((f16)o); } /// Returns a hash code for the half. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return (int)value; } } } #endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS