using System; using System.IO; namespace Sog { /// /// Represents a Q31.32 fixed-point number. /// [Serializable] public struct Fixed64 : IEquatable, IComparable { public long m_rawValue; private const long MAX_VALUE = long.MaxValue; private const long MIN_VALUE = long.MinValue; private const int NUM_BITS = 64; private const int FRACTIONAL_PLACES = 32; private const long ONE = 1L << FRACTIONAL_PLACES; private const long TEN = 10L << FRACTIONAL_PLACES; private const long HALF = 1L << (FRACTIONAL_PLACES - 1); private const long PI_TIMES_2 = 0x6487ED511; private const long PI = 0x3243F6A88; private const long PI_OVER_2 = 0x1921FB544; private const int LUT_SIZE = (int)(PI_OVER_2 >> 15); // Precision of this type is 2^-32, that is 2,3283064365386962890625E-10 public static readonly decimal Precision = (decimal)(new Fixed64(1L));//0.00000000023283064365386962890625m; public static readonly Fixed64 MaxValue = new Fixed64(MAX_VALUE - 1); public static readonly Fixed64 MinValue = new Fixed64(MIN_VALUE + 2); public static readonly Fixed64 One = new Fixed64(ONE); public static readonly Fixed64 Ten = new Fixed64(TEN); public static readonly Fixed64 Half = new Fixed64(HALF); public static readonly Fixed64 Zero = new Fixed64(); public static readonly Fixed64 PositiveInfinity = new Fixed64(MAX_VALUE); public static readonly Fixed64 NegativeInfinity = new Fixed64(MIN_VALUE + 1); public static readonly Fixed64 NaN = new Fixed64(MIN_VALUE); public static readonly Fixed64 EN1 = One / 10; public static readonly Fixed64 EN2 = One / 100; public static readonly Fixed64 EN3 = One / 1000; public static readonly Fixed64 EN4 = One / 10000; public static readonly Fixed64 EN5 = One / 100000; public static readonly Fixed64 EN6 = One / 1000000; public static readonly Fixed64 EN7 = One / 10000000; public static readonly Fixed64 EN8 = One / 100000000; public static readonly Fixed64 Epsilon = EN3; /// /// The value of Pi /// public static readonly Fixed64 Pi = new Fixed64(PI); public static readonly Fixed64 PiOver2 = new Fixed64(PI_OVER_2); public static readonly Fixed64 PiTimes2 = new Fixed64(PI_TIMES_2); public static readonly Fixed64 PiInv = (Fixed64)0.3183098861837906715377675267M; public static readonly Fixed64 PiOver2Inv = (Fixed64)0.6366197723675813430755350535M; public static readonly Fixed64 Deg2Rad = Pi / new Fixed64(180); //0.0174532924f public static readonly Fixed64 Rad2Deg = new Fixed64(180) / Pi; // public static readonly Fixed64 LutInterval = (LUT_SIZE - 1) / PiOver2; /// /// Returns a number indicating the sign of a Fix64 number. /// Returns 1 if the value is positive, 0 if is 0, and -1 if it is negative. /// public static int Sign(Fixed64 value) { return value.m_rawValue < 0 ? -1 : value.m_rawValue > 0 ? 1 : 0; } /// /// Returns the absolute value of a Fix64 number. /// Note: Abs(Fix64.MinValue) == Fix64.MaxValue. /// public static Fixed64 Abs(Fixed64 value) { if (value.m_rawValue == MIN_VALUE) { return MaxValue; } // branchless implementation, see http://www.strchr.com/optimized_abs_function var mask = value.m_rawValue >> 63; return new Fixed64((value.m_rawValue + mask) ^ mask); } /// /// Returns the absolute value of a Fix64 number. /// FastAbs(Fix64.MinValue) is undefined. /// public static Fixed64 FastAbs(Fixed64 value) { // branchless implementation, see http://www.strchr.com/optimized_abs_function var mask = value.m_rawValue >> 63; return new Fixed64((value.m_rawValue + mask) ^ mask); } /// /// Returns the largest integer less than or equal to the specified number. /// public static Fixed64 Floor(Fixed64 value) { // Just zero out the fractional part return new Fixed64((long)((ulong)value.m_rawValue & 0xFFFFFFFF00000000)); } /// /// Returns the smallest integral value that is greater than or equal to the specified number. /// public static Fixed64 Ceiling(Fixed64 value) { var hasFractionalPart = (value.m_rawValue & 0x00000000FFFFFFFF) != 0; return hasFractionalPart ? Floor(value) + One : value; } /// /// Rounds a value to the nearest integral value. /// If the value is halfway between an even and an uneven value, returns the even value. /// public static Fixed64 Round(Fixed64 value) { var fractionalPart = value.m_rawValue & 0x00000000FFFFFFFF; var integralPart = Floor(value); if (fractionalPart < 0x80000000) { return integralPart; } if (fractionalPart > 0x80000000) { return integralPart + One; } // if number is halfway between two values, round to the nearest even number // this is the method used by System.Math.Round(). return (integralPart.m_rawValue & ONE) == 0 ? integralPart : integralPart + One; } /// /// Adds x and y. Performs saturating addition, i.e. in case of overflow, /// rounds to MinValue or MaxValue depending on sign of operands. /// public static Fixed64 operator +(Fixed64 x, Fixed64 y) { return new Fixed64(x.m_rawValue + y.m_rawValue); } /// /// Adds x and y performing overflow checking. Should be inlined by the CLR. /// public static Fixed64 OverflowAdd(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; var sum = xl + yl; // if signs of operands are equal and signs of sum and x are different if (((~(xl ^ yl) & (xl ^ sum)) & MIN_VALUE) != 0) { sum = xl > 0 ? MAX_VALUE : MIN_VALUE; } return new Fixed64(sum); } /// /// Adds x and y witout performing overflow checking. Should be inlined by the CLR. /// public static Fixed64 FastAdd(Fixed64 x, Fixed64 y) { return new Fixed64(x.m_rawValue + y.m_rawValue); } /// /// Subtracts y from x. Performs saturating substraction, i.e. in case of overflow, /// rounds to MinValue or MaxValue depending on sign of operands. /// public static Fixed64 operator -(Fixed64 x, Fixed64 y) { return new Fixed64(x.m_rawValue - y.m_rawValue); } /// /// Subtracts y from x witout performing overflow checking. Should be inlined by the CLR. /// public static Fixed64 OverflowSub(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; var diff = xl - yl; // if signs of operands are different and signs of sum and x are different if ((((xl ^ yl) & (xl ^ diff)) & MIN_VALUE) != 0) { diff = xl < 0 ? MIN_VALUE : MAX_VALUE; } return new Fixed64(diff); } /// /// Subtracts y from x witout performing overflow checking. Should be inlined by the CLR. /// public static Fixed64 FastSub(Fixed64 x, Fixed64 y) { return new Fixed64(x.m_rawValue - y.m_rawValue); } static long AddOverflowHelper(long x, long y, ref bool overflow) { var sum = x + y; // x + y overflows if sign(x) ^ sign(y) != sign(sum) overflow |= ((x ^ y ^ sum) & MIN_VALUE) != 0; return sum; } public static Fixed64 operator *(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; var xlo = (ulong)(xl & 0x00000000FFFFFFFF); var xhi = xl >> FRACTIONAL_PLACES; var ylo = (ulong)(yl & 0x00000000FFFFFFFF); var yhi = yl >> FRACTIONAL_PLACES; var lolo = xlo * ylo; var lohi = (long)xlo * yhi; var hilo = xhi * (long)ylo; var hihi = xhi * yhi; var loResult = lolo >> FRACTIONAL_PLACES; var midResult1 = lohi; var midResult2 = hilo; var hiResult = hihi << FRACTIONAL_PLACES; var sum = (long)loResult + midResult1 + midResult2 + hiResult; Fixed64 result;// = default(FP); result.m_rawValue = sum; return result; } /// /// Performs multiplication without checking for overflow. /// Useful for performance-critical code where the values are guaranteed not to cause overflow /// public static Fixed64 OverflowMul(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; var xlo = (ulong)(xl & 0x00000000FFFFFFFF); var xhi = xl >> FRACTIONAL_PLACES; var ylo = (ulong)(yl & 0x00000000FFFFFFFF); var yhi = yl >> FRACTIONAL_PLACES; var lolo = xlo * ylo; var lohi = (long)xlo * yhi; var hilo = xhi * (long)ylo; var hihi = xhi * yhi; var loResult = lolo >> FRACTIONAL_PLACES; var midResult1 = lohi; var midResult2 = hilo; var hiResult = hihi << FRACTIONAL_PLACES; bool overflow = false; var sum = AddOverflowHelper((long)loResult, midResult1, ref overflow); sum = AddOverflowHelper(sum, midResult2, ref overflow); sum = AddOverflowHelper(sum, hiResult, ref overflow); bool opSignsEqual = ((xl ^ yl) & MIN_VALUE) == 0; // if signs of operands are equal and sign of result is negative, // then multiplication overflowed positively // the reverse is also true if (opSignsEqual) { if (sum < 0 || (overflow && xl > 0)) { return MaxValue; } } else { if (sum > 0) { return MinValue; } } // if the top 32 bits of hihi (unused in the result) are neither all 0s or 1s, // then this means the result overflowed. var topCarry = hihi >> FRACTIONAL_PLACES; if (topCarry != 0 && topCarry != -1 /*&& xl != -17 && yl != -17*/) { return opSignsEqual ? MaxValue : MinValue; } // If signs differ, both operands' magnitudes are greater than 1, // and the result is greater than the negative operand, then there was negative overflow. if (!opSignsEqual) { long posOp, negOp; if (xl > yl) { posOp = xl; negOp = yl; } else { posOp = yl; negOp = xl; } if (sum > negOp && negOp < -ONE && posOp > ONE) { return MinValue; } } return new Fixed64(sum); } /// /// Performs multiplication without checking for overflow. /// Useful for performance-critical code where the values are guaranteed not to cause overflow /// public static Fixed64 FastMul(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; var xlo = (ulong)(xl & 0x00000000FFFFFFFF); var xhi = xl >> FRACTIONAL_PLACES; var ylo = (ulong)(yl & 0x00000000FFFFFFFF); var yhi = yl >> FRACTIONAL_PLACES; var lolo = xlo * ylo; var lohi = (long)xlo * yhi; var hilo = xhi * (long)ylo; var hihi = xhi * yhi; var loResult = lolo >> FRACTIONAL_PLACES; var midResult1 = lohi; var midResult2 = hilo; var hiResult = hihi << FRACTIONAL_PLACES; var sum = (long)loResult + midResult1 + midResult2 + hiResult; Fixed64 result;// = default(FP); result.m_rawValue = sum; return result; //return new FP(sum); } //[MethodImplAttribute(MethodImplOptions.AggressiveInlining)] public static int CountLeadingZeroes(ulong x) { int result = 0; while ((x & 0xF000000000000000) == 0) { result += 4; x <<= 4; } while ((x & 0x8000000000000000) == 0) { result += 1; x <<= 1; } return result; } public static Fixed64 operator /(Fixed64 x, Fixed64 y) { var xl = x.m_rawValue; var yl = y.m_rawValue; if (yl == 0) { return MAX_VALUE; //throw new DivideByZeroException(); } var remainder = (ulong)(xl >= 0 ? xl : -xl); var divider = (ulong)(yl >= 0 ? yl : -yl); var quotient = 0UL; var bitPos = NUM_BITS / 2 + 1; // If the divider is divisible by 2^n, take advantage of it. while ((divider & 0xF) == 0 && bitPos >= 4) { divider >>= 4; bitPos -= 4; } while (remainder != 0 && bitPos >= 0) { int shift = CountLeadingZeroes(remainder); if (shift > bitPos) { shift = bitPos; } remainder <<= shift; bitPos -= shift; var div = remainder / divider; remainder = remainder % divider; quotient += div << bitPos; // Detect overflow if ((div & ~(0xFFFFFFFFFFFFFFFF >> bitPos)) != 0) { return ((xl ^ yl) & MIN_VALUE) == 0 ? MaxValue : MinValue; } remainder <<= 1; --bitPos; } // rounding ++quotient; var result = (long)(quotient >> 1); if (((xl ^ yl) & MIN_VALUE) != 0) { result = -result; } return new Fixed64(result); } public static Fixed64 operator %(Fixed64 x, Fixed64 y) { return new Fixed64( x.m_rawValue == MIN_VALUE & y.m_rawValue == -1 ? 0 : x.m_rawValue % y.m_rawValue); } /// /// Performs modulo as fast as possible; throws if x == MinValue and y == -1. /// Use the operator (%) for a more reliable but slower modulo. /// public static Fixed64 FastMod(Fixed64 x, Fixed64 y) { return new Fixed64(x.m_rawValue % y.m_rawValue); } public static Fixed64 operator -(Fixed64 x) { return x.m_rawValue == MIN_VALUE ? MaxValue : new Fixed64(-x.m_rawValue); } public static bool operator ==(Fixed64 x, Fixed64 y) { return x.m_rawValue == y.m_rawValue; } public static bool operator !=(Fixed64 x, Fixed64 y) { return x.m_rawValue != y.m_rawValue; } public static bool operator >(Fixed64 x, Fixed64 y) { return x.m_rawValue > y.m_rawValue; } public static bool operator <(Fixed64 x, Fixed64 y) { return x.m_rawValue < y.m_rawValue; } public static bool operator >=(Fixed64 x, Fixed64 y) { return x.m_rawValue >= y.m_rawValue; } public static bool operator <=(Fixed64 x, Fixed64 y) { return x.m_rawValue <= y.m_rawValue; } /// /// Returns the square root of a specified number. /// /// /// The argument was negative. /// public static Fixed64 Sqrt(Fixed64 x) { var xl = x.m_rawValue; if (xl < 0) { // We cannot represent infinities like Single and Double, and Sqrt is // mathematically undefined for x < 0. So we just throw an exception. throw new ArgumentOutOfRangeException("Negative value passed to Sqrt", "x"); } var num = (ulong)xl; var result = 0UL; // second-to-top bit var bit = 1UL << (NUM_BITS - 2); while (bit > num) { bit >>= 2; } // The main part is executed twice, in order to avoid // using 128 bit values in computations. for (var i = 0; i < 2; ++i) { // First we get the top 48 bits of the answer. while (bit != 0) { if (num >= result + bit) { num -= result + bit; result = (result >> 1) + bit; } else { result = result >> 1; } bit >>= 2; } if (i == 0) { // Then process it again to get the lowest 16 bits. if (num > (1UL << (NUM_BITS / 2)) - 1) { // The remainder 'num' is too large to be shifted left // by 32, so we have to add 1 to result manually and // adjust 'num' accordingly. // num = a - (result + 0.5)^2 // = num + result^2 - (result + 0.5)^2 // = num - result - 0.5 num -= result; num = (num << (NUM_BITS / 2)) - 0x80000000UL; result = (result << (NUM_BITS / 2)) + 0x80000000UL; } else { num <<= (NUM_BITS / 2); result <<= (NUM_BITS / 2); } bit = 1UL << (NUM_BITS / 2 - 2); } } // Finally, if next bit would have been 1, round the result upwards. if (num > result) { ++result; } return new Fixed64((long)result); } /// /// Returns the Sine of x. /// This function has about 9 decimals of accuracy for small values of x. /// It may lose accuracy as the value of x grows. /// Performance: about 25% slower than Math.Sin() in x64, and 200% slower in x86. /// public static Fixed64 Sin(Fixed64 x) { var clampedL = ClampSinValue(x.m_rawValue, out bool flipHorizontal, out bool flipVertical); var clamped = new Fixed64(clampedL); // Find the two closest values in the LUT and perform linear interpolation // This is what kills the performance of this function on x86 - x64 is fine though var rawIndex = FastMul(clamped, LutInterval); var roundedIndex = Round(rawIndex); var indexError = 0;//FastSub(rawIndex, roundedIndex); var nearestValue = new Fixed64(Fixed64Internal.SinLut[flipHorizontal ? Fixed64Internal.SinLut.Length - 1 - (int)roundedIndex : (int)roundedIndex]); var secondNearestValue = new Fixed64(Fixed64Internal.SinLut[flipHorizontal ? Fixed64Internal.SinLut.Length - 1 - (int)roundedIndex - Sign(indexError) : (int)roundedIndex + Sign(indexError)]); var delta = FastMul(indexError, FastAbs(FastSub(nearestValue, secondNearestValue))).m_rawValue; var interpolatedValue = nearestValue.m_rawValue + (flipHorizontal ? -delta : delta); var finalValue = flipVertical ? -interpolatedValue : interpolatedValue; Fixed64 a2 = new Fixed64(finalValue); return a2; } /// /// Returns a rough approximation of the Sine of x. /// This is at least 3 times faster than Sin() on x86 and slightly faster than Math.Sin(), /// however its accuracy is limited to 4-5 decimals, for small enough values of x. /// public static Fixed64 FastSin(Fixed64 x) { var clampedL = ClampSinValue(x.m_rawValue, out bool flipHorizontal, out bool flipVertical); // Here we use the fact that the SinLut table has a number of entries // equal to (PI_OVER_2 >> 15) to use the angle to index directly into it var rawIndex = (uint)(clampedL >> 15); if (rawIndex >= LUT_SIZE) { rawIndex = LUT_SIZE - 1; } var nearestValue = Fixed64Internal.SinLut[flipHorizontal ? Fixed64Internal.SinLut.Length - 1 - (int)rawIndex : (int)rawIndex]; return new Fixed64(flipVertical ? -nearestValue : nearestValue); } //[MethodImplAttribute(MethodImplOptions.AggressiveInlining)] public static long ClampSinValue(long angle, out bool flipHorizontal, out bool flipVertical) { // Clamp value to 0 - 2*PI using modulo; this is very slow but there's no better way AFAIK var clamped2Pi = angle % PI_TIMES_2; if (angle < 0) { clamped2Pi += PI_TIMES_2; } // The LUT contains values for 0 - PiOver2; every other value must be obtained by // vertical or horizontal mirroring flipVertical = clamped2Pi >= PI; // obtain (angle % PI) from (angle % 2PI) - much faster than doing another modulo var clampedPi = clamped2Pi; while (clampedPi >= PI) { clampedPi -= PI; } flipHorizontal = clampedPi >= PI_OVER_2; // obtain (angle % PI_OVER_2) from (angle % PI) - much faster than doing another modulo var clampedPiOver2 = clampedPi; if (clampedPiOver2 >= PI_OVER_2) { clampedPiOver2 -= PI_OVER_2; } return clampedPiOver2; } /// /// Returns the cosine of x. /// See Sin() for more details. /// public static Fixed64 Cos(Fixed64 x) { var xl = x.m_rawValue; var rawAngle = xl + (xl > 0 ? -PI - PI_OVER_2 : PI_OVER_2); Fixed64 a2 = Sin(new Fixed64(rawAngle)); return a2; } /// /// Returns a rough approximation of the cosine of x. /// See FastSin for more details. /// public static Fixed64 FastCos(Fixed64 x) { var xl = x.m_rawValue; var rawAngle = xl + (xl > 0 ? -PI - PI_OVER_2 : PI_OVER_2); return FastSin(new Fixed64(rawAngle)); } /// /// Returns the tangent of x. /// /// /// This function is not well-tested. It may be wildly inaccurate. /// public static Fixed64 Tan(Fixed64 x) { var clampedPi = x.m_rawValue % PI; var flip = false; if (clampedPi < 0) { clampedPi = -clampedPi; flip = true; } if (clampedPi > PI_OVER_2) { flip = !flip; clampedPi = PI_OVER_2 - (clampedPi - PI_OVER_2); } var clamped = new Fixed64(clampedPi); // Find the two closest values in the LUT and perform linear interpolation var rawIndex = FastMul(clamped, LutInterval); var roundedIndex = Round(rawIndex); var indexError = FastSub(rawIndex, roundedIndex); var nearestValue = new Fixed64(Fixed64Internal.TanLut[(int)roundedIndex]); var secondNearestValue = new Fixed64(Fixed64Internal.TanLut[(int)roundedIndex + Sign(indexError)]); var delta = FastMul(indexError, FastAbs(FastSub(nearestValue, secondNearestValue))).m_rawValue; var interpolatedValue = nearestValue.m_rawValue + delta; var finalValue = flip ? -interpolatedValue : interpolatedValue; Fixed64 a2 = new Fixed64(finalValue); return a2; } public static Fixed64 Atan(Fixed64 y) { return Atan2(y, 1); } public static Fixed64 Atan2(Fixed64 y, Fixed64 x) { var yl = y.m_rawValue; var xl = x.m_rawValue; if (xl == 0) { if (yl > 0) { return PiOver2; } if (yl == 0) { return Zero; } return -PiOver2; } Fixed64 atan; var z = y / x; Fixed64 sm = EN2 * 28; // Deal with overflow if (One + sm * z * z == MaxValue) { return y < Zero ? -PiOver2 : PiOver2; } if (Abs(z) < One) { atan = z / (One + sm * z * z); if (xl < 0) { if (yl < 0) { return atan - Pi; } return atan + Pi; } } else { atan = PiOver2 - z / (z * z + sm); if (yl < 0) { return atan - Pi; } } return atan; } public static Fixed64 Asin(Fixed64 value) { return FastSub(PiOver2, Acos(value)); } public static Fixed64 Acos(Fixed64 value) { if (value == 0) { return PiOver2; } bool flip = false; if (value < 0) { value = -value; flip = true; } // Find the two closest values in the LUT and perform linear interpolation var rawIndex = FastMul(value, LUT_SIZE); var roundedIndex = Round(rawIndex); if (roundedIndex >= LUT_SIZE) { roundedIndex = LUT_SIZE - 1; } var indexError = FastSub(rawIndex, roundedIndex); var nearestValue = new Fixed64(Fixed64Internal.AcosLut[(int)roundedIndex]); var nextIndex = (int)roundedIndex + Sign(indexError); if (nextIndex >= LUT_SIZE) { nextIndex = LUT_SIZE - 1; } var secondNearestValue = new Fixed64(Fixed64Internal.AcosLut[nextIndex]); var delta = FastMul(indexError, FastAbs(FastSub(nearestValue, secondNearestValue))).m_rawValue; Fixed64 interpolatedValue = new Fixed64(nearestValue.m_rawValue + delta); Fixed64 finalValue = flip ? (Pi - interpolatedValue) : interpolatedValue; return finalValue; } public static implicit operator Fixed64(long value) { return new Fixed64(value * ONE); } public static explicit operator long(Fixed64 value) { return value.m_rawValue >> FRACTIONAL_PLACES; } public static implicit operator Fixed64(float value) { return new Fixed64((long)(value * ONE)); } public static explicit operator float(Fixed64 value) { return (float)value.m_rawValue / ONE; } public static implicit operator Fixed64(double value) { return new Fixed64((long)(value * ONE)); } public static explicit operator double(Fixed64 value) { return (double)value.m_rawValue / ONE; } public static explicit operator Fixed64(decimal value) { return new Fixed64((long)(value * ONE)); } public static implicit operator Fixed64(int value) { return new Fixed64(value * ONE); } public static explicit operator decimal(Fixed64 value) { return (decimal)value.m_rawValue / ONE; } public float AsFloat() { return (float)this; } public bool IsInt() { return (m_rawValue & 0x00000000FFFFFFFFL) == 0L; } // 向下取整保留3位小数 //public float AsFloat3() //{ // return (long)((float)this * 1000) / 1000.0f; //} // 保留3位小数 public string AsFloat3() { long fractionalPart = m_rawValue & 0x00000000FFFFFFFFL; if (fractionalPart == 0L) { return (m_rawValue >> FRACTIONAL_PLACES).ToString(); } // 小数放大1000倍取整, 再除以1000后保留前面整数的部分 Fixed64 tmp = new Fixed64(fractionalPart); tmp = tmp * 1000; return string.Format("{0}.{1:D3}", m_rawValue >> FRACTIONAL_PLACES, tmp.m_rawValue >> FRACTIONAL_PLACES); } public int AsInt() { return (int)this; } public long AsLong() { return (long)this; } public double AsDouble() { return (double)this; } public decimal AsDecimal() { return (decimal)this; } public bool IsNaN() { return this == NaN; } public static float ToFloat(Fixed64 value) { return (float)value; } public static int ToInt(Fixed64 value) { return (int)value; } public static Fixed64 FromFloat(float value) { return value; } public static bool IsInfinity(Fixed64 value) { return value == NegativeInfinity || value == PositiveInfinity; } public static bool IsNaN(Fixed64 value) { return value == NaN; } public override bool Equals(object obj) { return obj is Fixed64 && ((Fixed64)obj).m_rawValue == m_rawValue; } public override int GetHashCode() { return m_rawValue.GetHashCode(); } public bool Equals(Fixed64 other) { return m_rawValue == other.m_rawValue; } public int CompareTo(Fixed64 other) { return m_rawValue.CompareTo(other.m_rawValue); } public override string ToString() { //return ((float)this).ToString(); return AsFloat3(); } public static Fixed64 FromRaw(long rawValue) { return new Fixed64(rawValue); } internal static void GenerateAcosLut() { using (var writer = new StreamWriter("Fix64AcosLut.cs")) { writer.Write( @"namespace FixMath.NET { partial struct Fix64 { public static readonly long[] AcosLut = new[] {"); int lineCounter = 0; for (int i = 0; i < LUT_SIZE; ++i) { var angle = i / ((float)(LUT_SIZE - 1)); if (lineCounter++ % 8 == 0) { writer.WriteLine(); writer.Write(" "); } var acos = Math.Acos(angle); var rawValue = ((Fixed64)acos).m_rawValue; writer.Write(string.Format("0x{0:X}L, ", rawValue)); } writer.Write( @" }; } }"); } } internal static void GenerateSinLut() { using (var writer = new StreamWriter("Fix64SinLut.cs")) { writer.Write( @"namespace FixMath.NET { partial struct Fix64 { public static readonly long[] SinLut = new[] {"); int lineCounter = 0; for (int i = 0; i < LUT_SIZE; ++i) { var angle = i * Math.PI * 0.5 / (LUT_SIZE - 1); if (lineCounter++ % 8 == 0) { writer.WriteLine(); writer.Write(" "); } var sin = Math.Sin(angle); var rawValue = ((Fixed64)sin).m_rawValue; writer.Write(string.Format("0x{0:X}L, ", rawValue)); } writer.Write( @" }; } }"); } } internal static void GenerateTanLut() { using (var writer = new StreamWriter("Fix64TanLut.cs")) { writer.Write( @"namespace FixMath.NET { partial struct Fix64 { public static readonly long[] TanLut = new[] {"); int lineCounter = 0; for (int i = 0; i < LUT_SIZE; ++i) { var angle = i * Math.PI * 0.5 / (LUT_SIZE - 1); if (lineCounter++ % 8 == 0) { writer.WriteLine(); writer.Write(" "); } var tan = Math.Tan(angle); if (tan > (double)MaxValue || tan < 0.0) { tan = (double)MaxValue; } var rawValue = (((decimal)tan > (decimal)MaxValue || tan < 0.0) ? MaxValue : tan).m_rawValue; writer.Write(string.Format("0x{0:X}L, ", rawValue)); } writer.Write( @" }; } }"); } } /// /// The underlying integer representation /// public long RawValue => m_rawValue; /// /// This is the constructor from raw value; it can only be used interally. /// /// Fixed64(long rawValue) { m_rawValue = rawValue; } Fixed64(int value) { m_rawValue = value * ONE; } } }