Files
valheim/Assets/Smooth Sync/Common/Required Internal Use Scripts/HalfHelper.cs
2025-07-04 14:16:14 +08:00

256 lines
8.8 KiB
C#

using System.IO;
using System.Runtime.InteropServices;
//using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization.Formatters.Binary;
namespace System
{
/// <summary>
/// Helper class for Half conversions and some low level operations.
/// This class is internally used in the Half class.
/// </summary>
/// <remarks>
/// References:
/// - Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
/// </remarks>
public static class HalfHelper
{
private static uint[] mantissaTable = GenerateMantissaTable();
private static uint[] exponentTable = GenerateExponentTable();
private static ushort[] offsetTable = GenerateOffsetTable();
private static ushort[] baseTable = GenerateBaseTable();
private static sbyte[] shiftTable = GenerateShiftTable();
[StructLayout(LayoutKind.Explicit)]
struct UIntFloat
{
[FieldOffset(0)]
public uint UIntValue;
[FieldOffset(0)]
public float FloatValue;
}
// Zeb stuff to get around unsafe code
static UIntFloat floatToIntConverter = new UIntFloat { FloatValue = 0 };
static uint FloatToUInt(float v)
{
floatToIntConverter.FloatValue = v;
return floatToIntConverter.UIntValue;
}
static float UIntToFloat(uint v)
{
floatToIntConverter.UIntValue = v;
return floatToIntConverter.FloatValue;
}
// End Zeb stuff to get around unsafe code
// Transforms the subnormal representation to a normalized one.
private static uint ConvertMantissa(int i)
{
uint m = (uint)(i << 13); // Zero pad mantissa bits
uint e = 0; // Zero exponent
// While not normalized
while ((m & 0x00800000) == 0)
{
e -= 0x00800000; // Decrement exponent (1<<23)
m <<= 1; // Shift mantissa
}
m &= unchecked((uint)~0x00800000); // Clear leading 1 bit
e += 0x38800000; // Adjust bias ((127-14)<<23)
return m | e; // Return combined number
}
private static uint[] GenerateMantissaTable()
{
uint[] mantissaTable = new uint[2048];
mantissaTable[0] = 0;
for (int i = 1; i < 1024; i++)
{
mantissaTable[i] = ConvertMantissa(i);
}
for (int i = 1024; i < 2048; i++)
{
mantissaTable[i] = (uint)(0x38000000 + ((i - 1024) << 13));
}
return mantissaTable;
}
private static uint[] GenerateExponentTable()
{
uint[] exponentTable = new uint[64];
exponentTable[0] = 0;
for (int i = 1; i < 31; i++)
{
exponentTable[i] = (uint)(i << 23);
}
exponentTable[31] = 0x47800000;
exponentTable[32] = 0x80000000;
for (int i = 33; i < 63; i++)
{
exponentTable[i] = (uint)(0x80000000 + ((i - 32) << 23));
}
exponentTable[63] = 0xc7800000;
return exponentTable;
}
private static ushort[] GenerateOffsetTable()
{
ushort[] offsetTable = new ushort[64];
offsetTable[0] = 0;
for (int i = 1; i < 32; i++)
{
offsetTable[i] = 1024;
}
offsetTable[32] = 0;
for (int i = 33; i < 64; i++)
{
offsetTable[i] = 1024;
}
return offsetTable;
}
private static ushort[] GenerateBaseTable()
{
ushort[] baseTable = new ushort[512];
for (int i = 0; i < 256; ++i)
{
sbyte e = (sbyte)(127 - i);
if (e > 24)
{ // Very small numbers map to zero
baseTable[i | 0x000] = 0x0000;
baseTable[i | 0x100] = 0x8000;
}
else if (e > 14)
{ // Small numbers map to denorms
baseTable[i | 0x000] = (ushort)(0x0400 >> (18 + e));
baseTable[i | 0x100] = (ushort)((0x0400 >> (18 + e)) | 0x8000);
}
else if (e >= -15)
{ // Normal numbers just lose precision
baseTable[i | 0x000] = (ushort)((15 - e) << 10);
baseTable[i | 0x100] = (ushort)(((15 - e) << 10) | 0x8000);
}
else if (e > -128)
{ // Large numbers map to Infinity
baseTable[i | 0x000] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
}
else
{ // Infinity and NaN's stay Infinity and NaN's
baseTable[i | 0x000] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
}
}
return baseTable;
}
private static sbyte[] GenerateShiftTable()
{
sbyte[] shiftTable = new sbyte[512];
for (int i = 0; i < 256; ++i)
{
sbyte e = (sbyte)(127 - i);
if (e > 24)
{ // Very small numbers map to zero
shiftTable[i | 0x000] = 24;
shiftTable[i | 0x100] = 24;
}
else if (e > 14)
{ // Small numbers map to denorms
shiftTable[i | 0x000] = (sbyte)(e - 1);
shiftTable[i | 0x100] = (sbyte)(e - 1);
}
else if (e >= -15)
{ // Normal numbers just lose precision
shiftTable[i | 0x000] = 13;
shiftTable[i | 0x100] = 13;
}
else if (e > -128)
{ // Large numbers map to Infinity
shiftTable[i | 0x000] = 24;
shiftTable[i | 0x100] = 24;
}
else
{ // Infinity and NaN's stay Infinity and NaN's
shiftTable[i | 0x000] = 13;
shiftTable[i | 0x100] = 13;
}
}
return shiftTable;
}
//public static unsafe uint FloatToUIntUnsafe(float single)
//{
// return *((uint*)&single);
//}
//public static unsafe float UIntToFloatUnsafe(uint half)
//{
// return *((float*)&half);
//}
// Noble Whale Studios made these safe
public static float HalfToSingle(Half half)
{
uint result = mantissaTable[offsetTable[half.internalValue >> 10] + (half.internalValue & 0x3ff)] + exponentTable[half.internalValue >> 10];
//return *((float*)&result);
return UIntToFloat(result);
}
public static Half SingleToHalf(float single)
{
//uint value = *((uint*)&single);
uint value = FloatToUInt(single);
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return Half.ToHalf(result);
}
// End Noble Whale Studios made these safe
// Noble Whale Studios' methods for compressing and decompressing floats to send over network
public static float Decompress(ushort compressedFloat)
{
uint result = mantissaTable[offsetTable[compressedFloat >> 10] + (compressedFloat & 0x3ff)] + exponentTable[compressedFloat >> 10];
return UIntToFloat(result);
}
public static ushort Compress(float uncompressedFloat)
{
uint value = FloatToUInt(uncompressedFloat);
return (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
}
// End Noble Whale Studios' methods for compressing and decompressing floats to send over network
public static Half Negate(Half half)
{
return Half.ToHalf((ushort)(half.internalValue ^ 0x8000));
}
public static Half Abs(Half half)
{
return Half.ToHalf((ushort)(half.internalValue & 0x7fff));
}
public static bool IsNaN(Half half)
{
return ((half.internalValue & 0x7fff) > 0x7c00);
}
public static bool IsInfinity(Half half)
{
return ((half.internalValue & 0x7fff) == 0x7c00);
}
public static bool IsPositiveInfinity(Half half)
{
return (half.internalValue == 0x7c00);
}
public static bool IsNegativeInfinity(Half half)
{
return (half.internalValue == 0xfc00);
}
}
}