11 public partial
struct FP : IEquatable<FP>, IComparable<FP>, ISerializationCallbackReceiver {
13 private long m_rawValue;
16 private string _serializedValue;
18 const long MAX_VALUE =
long.MaxValue;
19 const long MIN_VALUE =
long.MinValue;
20 const int NUM_BITS = 64;
21 const int FRACTIONAL_PLACES = 32;
22 const long ONE = 1L << FRACTIONAL_PLACES;
23 const long TEN = 10L << FRACTIONAL_PLACES;
24 const long HALF = 1L << (FRACTIONAL_PLACES - 1);
25 const long PI_TIMES_2 = 0x6487ED511;
26 const long PI = 0x3243F6A88;
27 const long PI_OVER_2 = 0x1921FB544;
28 const int LUT_SIZE = (int)(PI_OVER_2 >> 15);
31 public static readonly decimal Precision = (decimal)(
new FP(1L));
32 public static readonly
FP MaxValue =
new FP(MAX_VALUE-1);
33 public static readonly
FP MinValue =
new FP(MIN_VALUE+2);
34 public static readonly
FP One =
new FP(ONE);
35 public static readonly
FP Ten =
new FP(TEN);
36 public static readonly
FP Half =
new FP(HALF);
38 public static readonly
FP Zero =
new FP();
39 public static readonly
FP PositiveInfinity =
new FP(MAX_VALUE);
40 public static readonly
FP NegativeInfinity =
new FP(MIN_VALUE+1);
41 public static readonly
FP NaN =
new FP(MIN_VALUE);
43 public static readonly
FP EN1 =
FP.One / 10;
44 public static readonly
FP EN2 =
FP.One / 100;
45 public static readonly
FP EN3 =
FP.One / 1000;
46 public static readonly
FP EN4 =
FP.One / 10000;
47 public static readonly
FP EN5 =
FP.One / 100000;
48 public static readonly
FP EN6 =
FP.One / 1000000;
49 public static readonly
FP EN7 =
FP.One / 10000000;
50 public static readonly
FP EN8 =
FP.One / 100000000;
51 public static readonly
FP Epsilon =
FP.EN3;
56 public static readonly
FP Pi =
new FP(PI);
57 public static readonly
FP PiOver2 =
new FP(PI_OVER_2);
58 public static readonly
FP PiTimes2 =
new FP(PI_TIMES_2);
59 public static readonly
FP PiInv = (
FP)0.3183098861837906715377675267M;
60 public static readonly
FP PiOver2Inv = (
FP)0.6366197723675813430755350535M;
62 public static readonly
FP Deg2Rad = Pi /
new FP(180);
64 public static readonly
FP Rad2Deg =
new FP(180) /
Pi;
66 static readonly
FP LutInterval = (
FP)(LUT_SIZE - 1) / PiOver2;
68 public void OnBeforeSerialize() {
69 _serializedValue = m_rawValue.ToString();
72 public void OnAfterDeserialize() {
73 if (!
string.IsNullOrEmpty(_serializedValue)) {
74 m_rawValue =
long.Parse(_serializedValue);
84 value.m_rawValue < 0 ? -1 :
85 value.m_rawValue > 0 ? 1 :
95 if (value.m_rawValue == MIN_VALUE) {
100 var mask = value.m_rawValue >> 63;
101 return new FP((value.m_rawValue + mask) ^ mask);
110 var mask = value.m_rawValue >> 63;
111 return new FP((value.m_rawValue + mask) ^ mask);
120 return new FP((
long)((ulong)value.m_rawValue & 0xFFFFFFFF00000000));
127 var hasFractionalPart = (value.m_rawValue & 0x00000000FFFFFFFF) != 0;
128 return hasFractionalPart ?
Floor(value) + One : value;
136 var fractionalPart = value.m_rawValue & 0x00000000FFFFFFFF;
137 var integralPart =
Floor(value);
138 if (fractionalPart < 0x80000000) {
141 if (fractionalPart > 0x80000000) {
142 return integralPart + One;
146 return (integralPart.m_rawValue & ONE) == 0
148 : integralPart + One;
159 var xl = x.m_rawValue;
160 var yl = y.m_rawValue;
163 if (((~(xl ^ yl) & (xl ^ sum)) & MIN_VALUE) != 0) {
164 sum = xl > 0 ? MAX_VALUE : MIN_VALUE;
173 return new FP(x.m_rawValue + y.m_rawValue);
184 var xl = x.m_rawValue;
185 var yl = y.m_rawValue;
188 if ((((xl ^ yl) & (xl ^ diff)) & MIN_VALUE) != 0) {
189 diff = xl < 0 ? MIN_VALUE : MAX_VALUE;
198 return new FP(x.m_rawValue - y.m_rawValue);
201 static long AddOverflowHelper(
long x,
long y, ref
bool overflow) {
204 overflow |= ((x ^ y ^ sum) & MIN_VALUE) != 0;
208 public static FP operator *(
FP x,
FP y) {
212 var xl = x.m_rawValue;
213 var yl = y.m_rawValue;
215 var xlo = (ulong)(xl & 0x00000000FFFFFFFF);
216 var xhi = xl >> FRACTIONAL_PLACES;
217 var ylo = (ulong)(yl & 0x00000000FFFFFFFF);
218 var yhi = yl >> FRACTIONAL_PLACES;
220 var lolo = xlo * ylo;
221 var lohi = (long)xlo * yhi;
222 var hilo = xhi * (long)ylo;
223 var hihi = xhi * yhi;
225 var loResult = lolo >> FRACTIONAL_PLACES;
226 var midResult1 = lohi;
227 var midResult2 = hilo;
228 var hiResult = hihi << FRACTIONAL_PLACES;
230 bool overflow =
false;
231 var sum = AddOverflowHelper((
long)loResult, midResult1, ref overflow);
232 sum = AddOverflowHelper(sum, midResult2, ref overflow);
233 sum = AddOverflowHelper(sum, hiResult, ref overflow);
235 bool opSignsEqual = ((xl ^ yl) & MIN_VALUE) == 0;
241 if (sum < 0 || (overflow && xl > 0)) {
252 var topCarry = hihi >> FRACTIONAL_PLACES;
253 if (topCarry != 0 && topCarry != -1 ) {
254 return opSignsEqual ? MaxValue : MinValue;
268 if (sum > negOp && negOp < -ONE && posOp > ONE) {
282 var xl = x.m_rawValue;
283 var yl = y.m_rawValue;
285 var xlo = (ulong)(xl & 0x00000000FFFFFFFF);
286 var xhi = xl >> FRACTIONAL_PLACES;
287 var ylo = (ulong)(yl & 0x00000000FFFFFFFF);
288 var yhi = yl >> FRACTIONAL_PLACES;
290 var lolo = xlo * ylo;
291 var lohi = (long)xlo * yhi;
292 var hilo = xhi * (long)ylo;
293 var hihi = xhi * yhi;
295 var loResult = lolo >> FRACTIONAL_PLACES;
296 var midResult1 = lohi;
297 var midResult2 = hilo;
298 var hiResult = hihi << FRACTIONAL_PLACES;
300 var sum = (long)loResult + midResult1 + midResult2 + hiResult;
305 static int CountLeadingZeroes(ulong x) {
307 while ((x & 0xF000000000000000) == 0) { result += 4; x <<= 4; }
308 while ((x & 0x8000000000000000) == 0) { result += 1; x <<= 1; }
312 public static FP operator /(
FP x,
FP y) {
316 var xl = x.m_rawValue;
317 var yl = y.m_rawValue;
324 var remainder = (ulong)(xl >= 0 ? xl : -xl);
325 var divider = (ulong)(yl >= 0 ? yl : -yl);
327 var bitPos = NUM_BITS / 2 + 1;
331 while ((divider & 0xF) == 0 && bitPos >= 4) {
336 while (remainder != 0 && bitPos >= 0) {
337 int shift = CountLeadingZeroes(remainder);
338 if (shift > bitPos) {
344 var div = remainder / divider;
345 remainder = remainder % divider;
346 quotient += div << bitPos;
349 if ((div & ~(0xFFFFFFFFFFFFFFFF >> bitPos)) != 0) {
350 return ((xl ^ yl) & MIN_VALUE) == 0 ? MaxValue : MinValue;
359 var result = (long)(quotient >> 1);
360 if (((xl ^ yl) & MIN_VALUE) != 0) {
364 return new FP(result);
367 public static FP operator %(
FP x,
FP y) {
369 x.m_rawValue == MIN_VALUE & y.m_rawValue == -1 ?
371 x.m_rawValue % y.m_rawValue);
379 return new FP(x.m_rawValue % y.m_rawValue);
383 return x.m_rawValue == MIN_VALUE ? MaxValue :
new FP(-x.m_rawValue);
386 public static bool operator ==(
FP x,
FP y) {
387 return x.m_rawValue == y.m_rawValue;
390 public static bool operator !=(
FP x,
FP y) {
391 return x.m_rawValue != y.m_rawValue;
394 public static bool operator >(
FP x,
FP y) {
395 return x.m_rawValue > y.m_rawValue;
398 public static bool operator <(
FP x,
FP y) {
399 return x.m_rawValue < y.m_rawValue;
402 public static bool operator >=(
FP x,
FP y) {
403 return x.m_rawValue >= y.m_rawValue;
406 public static bool operator <=(
FP x,
FP y) {
407 return x.m_rawValue <= y.m_rawValue;
418 var xl = x.m_rawValue;
422 throw new ArgumentOutOfRangeException(
"Negative value passed to Sqrt",
"x");
429 var bit = 1UL << (NUM_BITS - 2);
437 for (var i = 0; i < 2; ++i) {
440 if (num >= result + bit) {
442 result = (result >> 1) + bit;
444 result = result >> 1;
451 if (num > (1UL << (NUM_BITS / 2)) - 1) {
459 num = (num << (NUM_BITS / 2)) - 0x80000000UL;
460 result = (result << (NUM_BITS / 2)) + 0x80000000UL;
462 num <<= (NUM_BITS / 2);
463 result <<= (NUM_BITS / 2);
466 bit = 1UL << (NUM_BITS / 2 - 2);
473 return new FP((
long)result);
483 bool flipHorizontal, flipVertical;
484 var clampedL = ClampSinValue(x.m_rawValue, out flipHorizontal, out flipVertical);
485 var clamped =
new FP(clampedL);
489 var rawIndex =
FastMul(clamped, LutInterval);
490 var roundedIndex =
Round(rawIndex);
493 var nearestValue =
new FP(SinLut[flipHorizontal ?
494 SinLut.Length - 1 - (
int)roundedIndex :
496 var secondNearestValue =
new FP(SinLut[flipHorizontal ?
497 SinLut.Length - 1 - (
int)roundedIndex -
Sign(indexError) :
498 (
int)roundedIndex +
Sign(indexError)]);
501 var interpolatedValue = nearestValue.m_rawValue + (flipHorizontal ? -delta : delta);
502 var finalValue = flipVertical ? -interpolatedValue : interpolatedValue;
503 FP a2 =
new FP(finalValue);
513 bool flipHorizontal, flipVertical;
514 var clampedL = ClampSinValue(x.m_rawValue, out flipHorizontal, out flipVertical);
518 var rawIndex = (uint)(clampedL >> 15);
519 if (rawIndex >= LUT_SIZE) {
520 rawIndex = LUT_SIZE - 1;
522 var nearestValue = SinLut[flipHorizontal ?
523 SinLut.Length - 1 - (int)rawIndex :
525 return new FP(flipVertical ? -nearestValue : nearestValue);
531 static long ClampSinValue(
long angle, out
bool flipHorizontal, out
bool flipVertical) {
533 var clamped2Pi = angle % PI_TIMES_2;
535 clamped2Pi += PI_TIMES_2;
540 flipVertical = clamped2Pi >= PI;
542 var clampedPi = clamped2Pi;
543 while (clampedPi >= PI) {
546 flipHorizontal = clampedPi >= PI_OVER_2;
548 var clampedPiOver2 = clampedPi;
549 if (clampedPiOver2 >= PI_OVER_2) {
550 clampedPiOver2 -= PI_OVER_2;
552 return clampedPiOver2;
560 var xl = x.m_rawValue;
561 var rawAngle = xl + (xl > 0 ? -PI - PI_OVER_2 : PI_OVER_2);
571 var xl = x.m_rawValue;
572 var rawAngle = xl + (xl > 0 ? -PI - PI_OVER_2 : PI_OVER_2);
583 var clampedPi = x.m_rawValue % PI;
586 clampedPi = -clampedPi;
589 if (clampedPi > PI_OVER_2) {
591 clampedPi = PI_OVER_2 - (clampedPi - PI_OVER_2);
594 var clamped =
new FP(clampedPi);
597 var rawIndex =
FastMul(clamped, LutInterval);
598 var roundedIndex =
Round(rawIndex);
599 var indexError =
FastSub(rawIndex, roundedIndex);
601 var nearestValue =
new FP(TanLut[(
int)roundedIndex]);
602 var secondNearestValue =
new FP(TanLut[(
int)roundedIndex +
Sign(indexError)]);
605 var interpolatedValue = nearestValue.m_rawValue + delta;
606 var finalValue = flip ? -interpolatedValue : interpolatedValue;
607 FP a2 =
new FP(finalValue);
611 public static FP Atan(
FP y) {
615 public static FP Atan2(
FP y,
FP x) {
616 var yl = y.m_rawValue;
617 var xl = x.m_rawValue;
632 if (One + sm * z * z == MaxValue) {
633 return y < Zero ? -PiOver2 : PiOver2;
637 atan = z / (One + sm * z * z);
645 atan = PiOver2 - z / (z * z + sm);
653 public static FP Asin(
FP value) {
654 return 2 * Atan(value / (1 +
FP.
Sqrt(
FP.One - value * value)));
657 public static FP Acos(
FP value) {
658 return 2 * Atan(
FP.
Sqrt(
FP.One - value * value) / (
FP.One + value));
661 public static implicit
operator FP(
long value) {
662 return new FP(value * ONE);
665 public static explicit operator long(
FP value) {
666 return value.m_rawValue >> FRACTIONAL_PLACES;
669 public static implicit
operator FP(
float value) {
670 return new FP((
long)(value * ONE));
673 public static explicit operator float(
FP value) {
674 return (
float)value.m_rawValue / ONE;
677 public static implicit
operator FP(
double value) {
678 return new FP((
long)(value * ONE));
681 public static explicit operator double(
FP value) {
682 return (
double)value.m_rawValue / ONE;
685 public static explicit operator FP(decimal value) {
686 return new FP((
long)(value * ONE));
689 public static implicit
operator FP(
int value) {
690 return new FP(value * ONE);
693 public static explicit operator decimal(
FP value) {
694 return (decimal)value.m_rawValue / ONE;
697 public float AsFloat() {
705 public long AsLong() {
709 public double AsDouble() {
713 public decimal AsDecimal() {
714 return (decimal)
this;
717 public static float ToFloat(
FP value) {
721 public static int ToInt(
FP value) {
725 public static FP FromFloat(
float value) {
729 public static bool IsInfinity(
FP value) {
730 return value == NegativeInfinity || value == PositiveInfinity;
733 public static bool IsNaN(
FP value) {
737 public override bool Equals(
object obj) {
738 return obj is
FP && ((
FP)obj).m_rawValue == m_rawValue;
741 public override int GetHashCode() {
742 return m_rawValue.GetHashCode();
745 public bool Equals(
FP other) {
746 return m_rawValue == other.m_rawValue;
749 public int CompareTo(
FP other) {
750 return m_rawValue.CompareTo(other.m_rawValue);
753 public override string ToString() {
754 return ((
float)
this).ToString();
757 public static FP FromRaw(
long rawValue) {
758 return new FP(rawValue);
761 internal static void GenerateSinLut() {
762 using (var writer =
new StreamWriter(
"Fix64SinLut.cs")) {
764 @"namespace FixMath.NET { 765 partial struct Fix64 { 766 public static readonly long[] SinLut = new[] {");
768 for (
int i = 0; i < LUT_SIZE; ++i) {
769 var angle = i * Math.PI * 0.5 / (LUT_SIZE - 1);
770 if (lineCounter++ % 8 == 0) {
774 var sin = Math.Sin(angle);
775 var rawValue = ((
FP)sin).m_rawValue;
776 writer.Write(
string.Format(
"0x{0:X}L, ", rawValue));
786 internal static void GenerateTanLut() {
787 using (var writer =
new StreamWriter(
"Fix64TanLut.cs")) {
789 @"namespace FixMath.NET { 790 partial struct Fix64 { 791 public static readonly long[] TanLut = new[] {");
793 for (
int i = 0; i < LUT_SIZE; ++i) {
794 var angle = i * Math.PI * 0.5 / (LUT_SIZE - 1);
795 if (lineCounter++ % 8 == 0) {
799 var tan = Math.Tan(angle);
800 if (tan > (
double)MaxValue || tan < 0.0) {
801 tan = (double)MaxValue;
803 var rawValue = (((decimal)tan > (decimal)MaxValue || tan < 0.0) ? MaxValue : (
FP)tan).m_rawValue;
804 writer.Write(
string.Format(
"0x{0:X}L, ", rawValue));
817 public long RawValue {
get {
return m_rawValue; } }
824 m_rawValue = rawValue;
825 _serializedValue =
"";
828 public FP(
int value) {
829 m_rawValue = value * ONE;
830 _serializedValue =
"";
static readonly FP Pi
The value of Pi
static FP Cos(FP x)
Returns the cosine of x. See Sin() for more details.
static FP FastCos(FP x)
Returns a rough approximation of the cosine of x. See FastSin for more details.
static FP FastAdd(FP x, FP y)
Adds x and y witout performing overflow checking. Should be inlined by the CLR.
static FP FastSin(FP x)
Returns a rough approximation of the Sine of x. This is at least 3 times faster than Sin() on x86 and...
static FP Tan(FP x)
Returns the tangent of x.
static int Sign(FP value)
Returns a number indicating the sign of a Fix64 number. Returns 1 if the value is positive...
static FP Sqrt(FP x)
Returns the square root of a specified number.
Represents a Q31.32 fixed-point number.
long RawValue
The underlying integer representation
static FP Ceiling(FP value)
Returns the smallest integral value that is greater than or equal to the specified number...
static FP FastAbs(FP value)
Returns the absolute value of a Fix64 number. FastAbs(Fix64.MinValue) is undefined.
static FP Round(FP value)
Rounds a value to the nearest integral value. If the value is halfway between an even and an uneven v...
static FP Sin(FP x)
Returns the Sine of x. This function has about 9 decimals of accuracy for small values of x...
static FP Abs(FP value)
Returns the absolute value of a Fix64 number. Note: Abs(Fix64.MinValue) == Fix64.MaxValue.
static FP operator+(FP x, FP y)
Adds x and y. Performs saturating addition, i.e. in case of overflow, rounds to MinValue or MaxValue ...
static FP FastMod(FP x, FP y)
Performs modulo as fast as possible; throws if x == MinValue and y == -1. Use the operator (%) for a ...
static FP operator-(FP x, FP y)
Subtracts y from x. Performs saturating substraction, i.e. in case of overflow, rounds to MinValue or...
static FP FastMul(FP x, FP y)
Performs multiplication without checking for overflow. Useful for performance-critical code where the...
FP(long rawValue)
This is the constructor from raw value; it can only be used interally.
static FP FastSub(FP x, FP y)
Subtracts y from x witout performing overflow checking. Should be inlined by the CLR.
static FP Floor(FP value)
Returns the largest integer less than or equal to the specified number.