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.
 
 
 
 
 
 

1068 lines
35 KiB

using System;
using System.IO;
namespace Sog
{
/// <summary>
/// Represents a Q31.32 fixed-point number.
/// </summary>
[Serializable]
public struct Fixed64 : IEquatable<Fixed64>, IComparable<Fixed64>
{
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;
/// <summary>
/// The value of Pi
/// </summary>
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;
/// <summary>
/// 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.
/// </summary>
public static int Sign(Fixed64 value)
{
return
value.m_rawValue < 0 ? -1 :
value.m_rawValue > 0 ? 1 :
0;
}
/// <summary>
/// Returns the absolute value of a Fix64 number.
/// Note: Abs(Fix64.MinValue) == Fix64.MaxValue.
/// </summary>
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);
}
/// <summary>
/// Returns the absolute value of a Fix64 number.
/// FastAbs(Fix64.MinValue) is undefined.
/// </summary>
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);
}
/// <summary>
/// Returns the largest integer less than or equal to the specified number.
/// </summary>
public static Fixed64 Floor(Fixed64 value)
{
// Just zero out the fractional part
return new Fixed64((long)((ulong)value.m_rawValue & 0xFFFFFFFF00000000));
}
/// <summary>
/// Returns the smallest integral value that is greater than or equal to the specified number.
/// </summary>
public static Fixed64 Ceiling(Fixed64 value)
{
var hasFractionalPart = (value.m_rawValue & 0x00000000FFFFFFFF) != 0;
return hasFractionalPart ? Floor(value) + One : value;
}
/// <summary>
/// Rounds a value to the nearest integral value.
/// If the value is halfway between an even and an uneven value, returns the even value.
/// </summary>
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;
}
/// <summary>
/// Adds x and y. Performs saturating addition, i.e. in case of overflow,
/// rounds to MinValue or MaxValue depending on sign of operands.
/// </summary>
public static Fixed64 operator +(Fixed64 x, Fixed64 y)
{
return new Fixed64(x.m_rawValue + y.m_rawValue);
}
/// <summary>
/// Adds x and y performing overflow checking. Should be inlined by the CLR.
/// </summary>
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);
}
/// <summary>
/// Adds x and y witout performing overflow checking. Should be inlined by the CLR.
/// </summary>
public static Fixed64 FastAdd(Fixed64 x, Fixed64 y)
{
return new Fixed64(x.m_rawValue + y.m_rawValue);
}
/// <summary>
/// Subtracts y from x. Performs saturating substraction, i.e. in case of overflow,
/// rounds to MinValue or MaxValue depending on sign of operands.
/// </summary>
public static Fixed64 operator -(Fixed64 x, Fixed64 y)
{
return new Fixed64(x.m_rawValue - y.m_rawValue);
}
/// <summary>
/// Subtracts y from x witout performing overflow checking. Should be inlined by the CLR.
/// </summary>
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);
}
/// <summary>
/// Subtracts y from x witout performing overflow checking. Should be inlined by the CLR.
/// </summary>
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;
}
/// <summary>
/// Performs multiplication without checking for overflow.
/// Useful for performance-critical code where the values are guaranteed not to cause overflow
/// </summary>
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);
}
/// <summary>
/// Performs multiplication without checking for overflow.
/// Useful for performance-critical code where the values are guaranteed not to cause overflow
/// </summary>
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);
}
/// <summary>
/// Performs modulo as fast as possible; throws if x == MinValue and y == -1.
/// Use the operator (%) for a more reliable but slower modulo.
/// </summary>
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;
}
/// <summary>
/// Returns the square root of a specified number.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// The argument was negative.
/// </exception>
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);
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Returns the cosine of x.
/// See Sin() for more details.
/// </summary>
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;
}
/// <summary>
/// Returns a rough approximation of the cosine of x.
/// See FastSin for more details.
/// </summary>
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));
}
/// <summary>
/// Returns the tangent of x.
/// </summary>
/// <remarks>
/// This function is not well-tested. It may be wildly inaccurate.
/// </remarks>
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������ǰ�������IJ���
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(
@"
};
}
}");
}
}
/// <summary>
/// The underlying integer representation
/// </summary>
public long RawValue => m_rawValue;
/// <summary>
/// This is the constructor from raw value; it can only be used interally.
/// </summary>
/// <param name="rawValue"></param>
Fixed64(long rawValue)
{
m_rawValue = rawValue;
}
Fixed64(int value)
{
m_rawValue = value * ONE;
}
}
}