You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
7.0 KiB
198 lines
7.0 KiB
using System.Runtime.InteropServices; |
|
|
|
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> |
|
[ComVisible(false)] |
|
internal 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(); |
|
|
|
// 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 float HalfToSingle(Half half) |
|
{ |
|
uint result = mantissaTable[offsetTable[half.value >> 10] + (half.value & 0x3ff)] + exponentTable[half.value >> 10]; |
|
return *((float*)&result); |
|
} |
|
public static unsafe Half SingleToHalf(float single) |
|
{ |
|
uint value = *((uint*)&single); |
|
|
|
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23])); |
|
return Half.ToHalf(result); |
|
} |
|
|
|
public static Half Negate(Half half) |
|
{ |
|
return Half.ToHalf((ushort)(half.value ^ 0x8000)); |
|
} |
|
public static Half Abs(Half half) |
|
{ |
|
return Half.ToHalf((ushort)(half.value & 0x7fff)); |
|
} |
|
|
|
public static bool IsNaN(Half half) |
|
{ |
|
return ((half.value & 0x7fff) > 0x7c00); |
|
} |
|
public static bool IsInfinity(Half half) |
|
{ |
|
return ((half.value & 0x7fff) == 0x7c00); |
|
} |
|
public static bool IsPositiveInfinity(Half half) |
|
{ |
|
return (half.value == 0x7c00); |
|
} |
|
public static bool IsNegativeInfinity(Half half) |
|
{ |
|
return (half.value == 0xfc00); |
|
} |
|
} |
|
}
|
|
|