using System;
namespace Syroot.IOExtension
{
///
/// Represents a 16-bit half-precision floating point value according to the IEEE 754 standard.
///
///
/// Examples:
/// SEEEEEFF_FFFFFFFF
/// 0b00000000_00000000 = 0
/// 1b00000000_00000000 = -0
/// 0b00111100_00000000 = 1
/// 0b11000000_00000000 = -2
/// 0b11111011_11111111 = 65504 (MaxValue)
/// 0b01111100_00000000 = PositiveInfinity
/// 0b11111100_00000000 = NegativeInfinity
///
public struct Half
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
/* ///
/// Represents the smallest positive value greater than zero.
///
public static readonly Half Epsilon = new Half(1);
///
/// Represents the largest possible value of .
///
public static readonly Half MaxValue = new Half(0b01111011_11111111);
///
/// Represents the smallest possible value of .
///
public static readonly Half MinValue = new Half(0b11111011_11111111);
///
/// Represents not a number (NaN).
///
public static readonly Half NaN = new Half(0b11111110_00000000);
///
/// Represents negative infinity.
///
public static readonly Half NegativeInfinity = new Half(0b11111100_00000000);
///
/// Represents positive infinity.
///
public static readonly Half PositiveInfinity = new Half(0b01111100_00000000);*/
private static readonly uint[] _mantissaTable;
private static readonly uint[] _exponentTable;
private static readonly uint[] _offsetTable;
private static readonly ushort[] _baseTable;
private static readonly byte[] _shiftTable;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
///
/// Initializes a new instance of the struct from the given
/// representation.
///
/// The raw representation of the internally stored bits.
internal Half(ushort raw)
{
Raw = raw;
}
static Half()
{
int i;
// Generate tables for Half to Single conversions.
// Generate the mantissa table.
_mantissaTable = new uint[2048];
// 0 => 0
_mantissaTable[0] = 0;
// Transform subnormal to normalized.
for (i = 1; i < 1024; i++)
{
uint m = ((uint)i) << 13;
uint e = 0;
while ((m & 0x00800000) == 0)
{
e -= 0x00800000;
m <<= 1;
}
m &= ~0x00800000U;
e += 0x38800000;
_mantissaTable[i] = m | e;
}
// Normal case.
for (i = 1024; i < 2048; i++)
{
_mantissaTable[i] = 0x38000000 + (((uint)(i - 1024)) << 13);
}
// Generate the exponent table.
_exponentTable = new uint[64];
// 0 => 0
_exponentTable[0] = 0;
for (i = 1; i < 63; i++)
{
if (i < 31)
{
// Positive numbers.
_exponentTable[i] = ((uint)i) << 23;
}
else
{
// Negative numbers.
_exponentTable[i] = 0x80000000 + (((uint)(i - 32)) << 23);
}
}
_exponentTable[31] = 0x47800000;
_exponentTable[32] = 0x80000000;
_exponentTable[63] = 0xC7800000;
// Generate the offset table.
_offsetTable = new uint[64];
_offsetTable[0] = 0;
for (i = 1; i < 64; i++)
{
_offsetTable[i] = 1024;
}
_offsetTable[32] = 0;
// Generate tables for Single to Half conversions.
//Generate the base and shift tables.
_baseTable = new ushort[512];
_shiftTable = new byte[512];
for (i = 0; i < 256; i++)
{
int e = i - 127;
if (e < -24)
{
// Very small numbers map to zero.
_baseTable[i | 0x000] = 0x0000;
_baseTable[i | 0x100] = 0x8000;
_shiftTable[i | 0x000] = 24;
_shiftTable[i | 0x100] = 24;
}
else if (e < -14)
{
// Small numbers map to denorms.
_baseTable[i | 0x000] = (ushort)(0x0400 >> (-e - 14));
_baseTable[i | 0x100] = (ushort)((0x0400 >> (-e - 14)) | 0x8000);
_shiftTable[i | 0x000] = (byte)(-e - 1);
_shiftTable[i | 0x100] = (byte)(-e - 1);
}
else if (e <= 15)
{
// Normal numbers just lose precision.
_baseTable[i | 0x000] = (ushort)((e + 15) << 10);
_baseTable[i | 0x100] = (ushort)(((e + 15) << 10) | 0x8000);
_shiftTable[i | 0x000] = 13;
_shiftTable[i | 0x100] = 13;
}
else if (e < 128)
{
// Large numbers map to Infinity.
_baseTable[i | 0x000] = 0x7C00;
_baseTable[i | 0x100] = 0xFC00;
_shiftTable[i | 0x000] = 24;
_shiftTable[i | 0x100] = 24;
}
else
{
// Infinity and NaN's stay Infinity and NaN's.
_baseTable[i | 0x000] = 0x7C00;
_baseTable[i | 0x100] = 0xFC00;
_shiftTable[i | 0x000] = 13;
_shiftTable[i | 0x100] = 13;
}
}
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
///
/// Gets the internally stored value to represent the instance.
///
/// Signed to get arithmetic rather than logical shifts.
internal ushort Raw { get; private set; }
// ---- OPERATORS ----------------------------------------------------------------------------------------------
///
/// Returns the given .
///
/// The .
/// The result.
public static Half operator +(Half a)
{
return a;
}
///
/// Adds the first to the second one.
///
/// The first .
/// The second .
/// The addition result.
public static Half operator +(Half a, Half b)
{
return (Half)((float)a + (float)b);
}
///
/// Negates the given .
///
/// The to negate.
/// The negated result.
public static Half operator -(Half a)
{
return new Half((ushort)(a.Raw ^ 0x8000));
}
///
/// Subtracts the first from the second one.
///
/// The first .
/// The second .
/// The subtraction result.
public static Half operator -(Half a, Half b)
{
return (Half)((float)a - (float)b);
}
///
/// Multiplicates the first by the second one.
///
/// The first .
/// The second .
/// The multiplication result.
public static Half operator *(Half a, Half b)
{
return (Half)((float)a * (float)b);
}
///
/// Divides the first through the second one.
///
/// The first .
/// The second .
/// The division result.
public static Half operator /(Half a, Half b)
{
return (Half)((float)a / (float)b);
}
///
/// Gets a value indicating whether the first specified is the same as the second
/// specified .
///
/// The first to compare.
/// The second to compare.
/// true, if both are the same.
public static bool operator ==(Half a, Half b)
{
return a.Equals(b);
}
///
/// Gets a value indicating whether the first specified is not the same as the second
/// specified .
///
/// The first to compare.
/// The second to compare.
/// true, if both are not the same.
public static bool operator !=(Half a, Half b)
{
return !a.Equals(b);
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static explicit operator Half(Int32 value)
{
return (Half)(float)value;
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static explicit operator Half(Double value)
{
return (Half)(float)value;
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static explicit operator Half(Single value)
{
uint uint32 = ((DWord)value).UInt32;
return new Half((ushort)(_baseTable[(uint32 >> 23) & 0x01FF]
+ ((uint32 & 0x007FFFFF) >> _shiftTable[(uint32 >> 23) & 0x01FF])));
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static implicit operator Double(Half value)
{
return (float)value;
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static explicit operator Int32(Half value)
{
return (int)(float)value;
}
///
/// Converts the given value to a instance.
///
/// The value to represent in the new
/// instance.
public static implicit operator Single(Half value)
{
DWord result = _mantissaTable[_offsetTable[value.Raw >> 10] + (((uint)value.Raw) & 0x03FF)]
+ _exponentTable[value.Raw >> 10];
return result.Single;
}
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
///
/// Gets a value indicating whether this is the same as the second specified
/// .
///
/// The object to compare, if it is a .
/// true, if both are the same.
public override bool Equals(object obj)
{
if (!(obj is Half))
{
return false;
}
Half half = (Half)obj;
return Equals(half);
}
///
/// Gets a hash code as an indication for object equality.
///
/// The hash code.
public override int GetHashCode()
{
return Raw;
}
///
/// Gets a string describing this .
///
/// A string describing this .
public override string ToString()
{
return ((double)this).ToString();
}
///
/// Indicates whether the current is equal to another .
///
/// A to compare with this .
/// true if the current is equal to the other parameter; otherwise, false.
///
public bool Equals(Half other)
{
return Equals(Raw == other.Raw);
}
/* ///
/// Returns a value indicating whether the specified number evaluates to not a number ().
///
/// A half-precision floating-point number.
/// true if value evaluates to not a number (); otherwise false.
public static bool IsNaN(Half half)
{
return (half.Raw & 0x7FFF) > PositiveInfinity.Raw;
}
///
/// Returns a value indicating whether the specified number evaluates to negative or positive infinity.
///
/// A half-precision floating-point number.
/// true if half evaluates to or ;
/// otherwise false.
public static bool IsInfinity(Half half)
{
return (half.Raw & 0x7FFF) == PositiveInfinity.Raw;
}
///
/// Returns a value indicating whether the specified number evaluates to negative infinity.
///
/// A half-precision floating-point number.
/// true if half evaluates to ; otherwise false.
public static bool IsNegativeInfinity(Half half)
{
return half.Raw == NegativeInfinity.Raw;
}
///
/// Returns a value indicating whether the specified number evaluates to positive infinity.
///
/// A half-precision floating-point number.
/// true if half evaluates to ; otherwise false.
public static bool IsPositiveInfinity(Half half)
{
return half.Raw == PositiveInfinity.Raw;
}*/
}
}