diff --git a/Core/Simulation/Math/FixedMath.cs b/Core/Simulation/Math/FixedMath.cs index 34c1b009..388666ae 100755 --- a/Core/Simulation/Math/FixedMath.cs +++ b/Core/Simulation/Math/FixedMath.cs @@ -11,10 +11,15 @@ using System.Collections; using System; -namespace Lockstep -{ - public static class FixedMath - { +namespace Lockstep { + /// + /// Utility class for fixed point numbers. + /// The lower (right) 16 bits are used as the fraction + /// component and the upper (left) 48 bits are used as the decimal + /// and sign. The numbers use 2-s compliment so they can + /// be added and subtracted like normal long values. + /// + public static class FixedMath { #region Meta @@ -37,13 +42,11 @@ public static class FixedMath /// Create a fixed point number from an integer. /// /// Integer. - public static long Create(long integer) - { + public static long Create(long integer) { return integer << SHIFT_AMOUNT; } - public static long Create(float singleFloat) - { + public static long Create(float singleFloat) { return (long)((double)singleFloat * One); } @@ -51,8 +54,7 @@ public static long Create(float singleFloat) /// Create a fixed point number from a double. /// /// Double float. - public static long Create(double doubleFloat) - { + public static long Create(double doubleFloat) { return (long)(doubleFloat * One); } @@ -61,8 +63,7 @@ public static long Create(double doubleFloat) /// /// Whole. /// Fraction. - public static long Create(long Numerator, long Denominator) - { + public static long Create(long Numerator, long Denominator) { return (Numerator << SHIFT_AMOUNT) / Denominator; } @@ -72,27 +73,20 @@ public static long Create(long Numerator, long Denominator) /// true, if parse was tried, false otherwise. /// S. /// Result. - public static bool TryParse(string s, out long result) - { + public static bool TryParse(string s, out long result) { string[] NewValues = s.Split('.'); - if (NewValues.Length <= 2) - { + if (NewValues.Length <= 2) { long Whole; - if (long.TryParse(NewValues [0], out Whole)) - { - if (NewValues.Length == 1) - { + if (long.TryParse(NewValues[0], out Whole)) { + if (NewValues.Length == 1) { result = Whole << SHIFT_AMOUNT; return true; - } else - { + } else { long Numerator; - if (long.TryParse(NewValues [1], out Numerator)) - { - int fractionDigits = NewValues [1].Length; + if (long.TryParse(NewValues[1], out Numerator)) { + int fractionDigits = NewValues[1].Length; long Denominator = 1; - for (int i = 0; i < fractionDigits; i++) - { + for (int i = 0; i < fractionDigits; i++) { Denominator *= 10; } result = (Whole << SHIFT_AMOUNT) + FixedMath.Create(Numerator, Denominator); @@ -114,8 +108,7 @@ public static bool TryParse(string s, out long result) /// /// f1. /// f2. - public static long Add(this long f1, long f2) - { + public static long Add(this long f1, long f2) { return f1 + f2; } @@ -124,8 +117,7 @@ public static long Add(this long f1, long f2) /// /// f1. /// f2. - public static long Sub(this long f1, long f2) - { + public static long Sub(this long f1, long f2) { return f1 - f2; } @@ -134,13 +126,11 @@ public static long Sub(this long f1, long f2) /// /// f1. /// f2. - public static long Mul(this long f1, long f2) - { + public static long Mul(this long f1, long f2) { return (f1 * f2) >> SHIFT_AMOUNT; } - public static long Mul(this long f1, int intr) - { + public static long Mul(this long f1, int intr) { return (f1 * intr); } @@ -149,8 +139,7 @@ public static long Mul(this long f1, int intr) /// /// f1. /// f2. - public static long Div(this long f1, long f2) - { + public static long Div(this long f1, long f2) { return (f1 << SHIFT_AMOUNT) / f2; } @@ -159,13 +148,11 @@ public static long Div(this long f1, long f2) /// /// f1. /// f2. - public static long Remainder(this long f1, long f2) - { + public static long Remainder(this long f1, long f2) { return f1 % f2; } - public static long Mod(this long f1, long f2) - { + public static long Mod(this long f1, long f2) { long f = f1 % f2; return f < 0 ? f + f2 : f; } @@ -177,47 +164,38 @@ public static long Mod(this long f1, long f2) static long n, n1; - public static long Sqrt(long f1) - { + public static long Sqrt(long f1) { if (f1 == 0) return 0; n = (f1 >> 1) + 1; n1 = (n + (f1 / n)) >> 1; - while (n1 < n) - { + while (n1 < n) { n = n1; n1 = (n + (f1 / n)) >> 1; } return n << (SHIFT_AMOUNT / 2); } - - - public static long Normalized(this long f1) - { + public static long Normalized(this long f1) { return f1 >> FixedMath.SHIFT_AMOUNT; } - public static long Abs(this long f1) - { + public static long Abs(this long f1) { return f1 < 0 ? -f1 : f1; } - public static bool AbsMoreThan(this long f1, long f2) - { + public static bool AbsMoreThan(this long f1, long f2) { if (f1 < 0) { return -f1 > f2; - } - else { + } else { return f1 > f2; } } - public static bool AbsLessThan (this long f1, long f2) { + public static bool AbsLessThan(this long f1, long f2) { if (f1 < 0) { return -f1 < f2; - } - else { + } else { return f1 < f2; } } @@ -230,8 +208,7 @@ public static bool AbsLessThan (this long f1, long f2) { /// Truncate the specified fixed-point number. /// /// F1. - public static long Truncate(long f1) - { + public static long Truncate(long f1) { return ((f1) >> SHIFT_AMOUNT) << SHIFT_AMOUNT; } @@ -239,28 +216,23 @@ public static long Truncate(long f1) /// Round the specified fixed point number. /// /// F1. - public static long Round(long f1) - { + public static long Round(long f1) { return ((f1 + FixedMath.Half - 1) >> SHIFT_AMOUNT) << SHIFT_AMOUNT; } - - /// /// Ceil the specified fixed point number. /// /// F1. - public static long Ceil(long f1) - { + public static long Ceil(long f1) { return ((f1 + One - 1) >> SHIFT_AMOUNT) << SHIFT_AMOUNT; } - public static long Floor (long f1) { + public static long Floor(long f1) { return ((f1) >> SHIFT_AMOUNT) << SHIFT_AMOUNT; } - public static long Lerp(long from, long to, long t) - { + public static long Lerp(long from, long to, long t) { if (t >= One) return to; else if (t <= 0) @@ -268,38 +240,36 @@ public static long Lerp(long from, long to, long t) return (to * t + from * (One - t)) >> SHIFT_AMOUNT; } - public static long Min(this long f1, long f2) - { + public static long Min(this long f1, long f2) { return f1 <= f2 ? f1 : f2; } - public static long Max(this long f1, long f2) - { + public static long Max(this long f1, long f2) { return f1 >= f2 ? f1 : f2; } - public static long Clamp (this long f1, long min, long max) { - if (f1 < min) return min; - if (f1 > max) return max; + + public static long Clamp(this long f1, long min, long max) { + if (f1 < min) + return min; + if (f1 > max) + return max; return f1; } - public static double ToFormattedDouble(this long f1) - { + + public static double ToFormattedDouble(this long f1) { return Math.Round(FixedMath.ToDouble(f1), 2, MidpointRounding.AwayFromZero); } - public static bool MoreThanEpsilon(this long f1) - { + public static bool MoreThanEpsilon(this long f1) { return f1 > Epsilon || f1 < Epsilon; } - public static long MoveTowards (long from, long to, long maxAmount) { - if (from < to) - { + public static long MoveTowards(long from, long to, long maxAmount) { + if (from < to) { from += maxAmount; if (from > to) from = to; - } - else if (from > to) { + } else if (from > to) { from -= maxAmount; if (from < to) from = to; @@ -310,7 +280,8 @@ public static long MoveTowards (long from, long to, long maxAmount) { #endregion #region Convert - public static int Sign (this long f1) { + + public static int Sign(this long f1) { if (f1 > 0) return 1; else if (f1 == 0) @@ -319,18 +290,15 @@ public static int Sign (this long f1) { return -1; } - public static int ToInt(this long f1) - { + public static int ToInt(this long f1) { return (int)(f1 >> SHIFT_AMOUNT); } - public static int RoundToInt(this long f1) - { + public static int RoundToInt(this long f1) { return (int)((f1 + Half - 1) >> SHIFT_AMOUNT); } - public static int CeilToInt(this long f1) - { + public static int CeilToInt(this long f1) { return (int)((f1 + One - 1) >> SHIFT_AMOUNT); } @@ -339,8 +307,7 @@ public static int CeilToInt(this long f1) /// /// The double. /// f1. - public static double ToDouble(this long f1) - { + public static double ToDouble(this long f1) { return (f1 / OneD); } @@ -349,13 +316,11 @@ public static double ToDouble(this long f1) /// /// The float. /// f1. - public static float ToFloat(this long f1) - { + public static float ToFloat(this long f1) { return (float)(f1 / OneD); } - public static float ToPreciseFloat(this long f1) - { + public static float ToPreciseFloat(this long f1) { return (float)ToDouble(f1); } @@ -365,19 +330,14 @@ public static float ToPreciseFloat(this long f1) /// The string. /// f1. - public static string GetString(this long f1) - { + public static string GetString(this long f1) { return (System.Math.Round((f1) / OneD, 4, System.MidpointRounding.AwayFromZero)).ToString(); } - - #endregion - public static class Trig - { - public static long Sin(long theta) - { + public static class Trig { + public static long Sin(long theta) { //Taylor series cuz easy //TODO: Profiling //Note: Max 4 multiplications before overflow @@ -400,7 +360,7 @@ public static long Sin(long theta) n *= thetaSquared; n >>= shift; const long Factorial7 = Factorial5 * 6 * 7; - result -= n / Factorial7; + result -= n / Factorial7; #if true || HIGH_ACCURACY //Required or there'll be .07 inaccuracy @@ -411,15 +371,17 @@ public static long Sin(long theta) #endif return result; } + public static long Cos(long theta) { - return Sin (theta - FixedMath.Pi / 2); + return Sin(theta - FixedMath.Pi / 2); } - public static long SinToCos(long sin) - { - return Sqrt(FixedMath.One - (sin.Mul( sin)).Normalized()); + + public static long SinToCos(long sin) { + return Sqrt(FixedMath.One - (sin.Mul(sin)).Normalized()); } - public static long Tan (long theta) { + + public static long Tan(long theta) { return Sin(theta).Div(Cos(theta)); } } diff --git a/Core/Simulation/Math/Tests.meta b/Core/Simulation/Math/Tests.meta new file mode 100644 index 00000000..2024fe7b --- /dev/null +++ b/Core/Simulation/Math/Tests.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 395a367a4de0e4d0baf5153e2a59fabb +folderAsset: yes +timeCreated: 1468530563 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Core/Simulation/Math/Tests/Editor.meta b/Core/Simulation/Math/Tests/Editor.meta new file mode 100644 index 00000000..2225e432 --- /dev/null +++ b/Core/Simulation/Math/Tests/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b77cfd3739c7b42c9805fe5a03b98222 +folderAsset: yes +timeCreated: 1468530563 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs b/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs new file mode 100644 index 00000000..098ee305 --- /dev/null +++ b/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs @@ -0,0 +1,123 @@ +using System; +using UnityEngine; +using UnityEditor; +using NUnit.Framework; +using Lockstep; + +namespace MonoTests.Lockstep { + [TestFixture] + public class FixedMathTest { + + [SetUp] + public void GetReady() { + } + + [TearDown] + public void Clean() { + } + + [Test] + public void CreateLongTest() { + Assert.AreEqual(0L, FixedMath.Create(0L)); + Assert.AreEqual(1L << 16, FixedMath.Create(1L)); + Assert.AreEqual(-1L << 16, FixedMath.Create(-1L)); + Assert.AreEqual(132414LL << 16, FixedMath.Create(132414L)); + } + + [Test] + public void AddSubIntTest() { + long n1 = FixedMath.Create(-1L); + long p1 = FixedMath.Create(1L); + + // Assert 1 + -1 = 0 + Assert.AreEqual(0, n1.Add(p1)); + + // Assert 1 + 1 = 2 + Assert.AreEqual(2 << 16, p1.Add(p1)); + + // Assert -1 + -1 = -2 + Assert.AreEqual(-2 << 16, n1.Add(n1)); + + // Assert 12 - 7 = 5 + Assert.AreEqual(5 << 16, FixedMath.Create(12L).Sub(FixedMath.Create(7L))); + + // Assert 33 - 66 = -33 + Assert.AreEqual(-33 << 16, FixedMath.Create(33L).Sub(FixedMath.Create(66L))); + + // Assert -33 - -66 = 33 + Assert.AreEqual(33 << 16, FixedMath.Create(-33L).Sub(FixedMath.Create(-66L))); + } + + [Test] + public void CreateFloatTest() { + // The left 48 bits in the number are the whole number + // and the right 16 are the fraction. This test ensures + // the fractional numbers are being assigned correctly. + Assert.AreEqual(0, FixedMath.Create(0f)); + // Assert 1.5 = ..001|100.. + Assert.AreEqual((1 << 16) + (1 << 15), FixedMath.Create(1.5f)); + // Assert .75 = ..0000|1100.. + Assert.AreEqual((1 << 14) + (1 << 15), FixedMath.Create(.75f)); + Assert.AreEqual((1 << 26) + (1 << 15), FixedMath.Create(1024.5f)); + Assert.AreEqual(-((1 << 26) + (1 << 15)), FixedMath.Create(-1024.5f)); + } + + [Test] + public void AddSubFloatTest() { + long half = FixedMath.Create(0.5f); + Assert.AreEqual(1 << 16, half.Add(half)); + + long quarter = FixedMath.Create(0.5f); + Assert.AreEqual(1 << 16, half.Add(half)); + } + + [Test] + public void RoundTest() { + // Keep in mind the current rounding uses 0.5 -> 0 + Assert.AreEqual(0, FixedMath.Round(FixedMath.Create(0.5f))); + Assert.AreEqual(1 << 16, FixedMath.Round(FixedMath.Create(0.6f))); + Assert.AreEqual(0, FixedMath.Round(FixedMath.Create(0.4f))); + Assert.AreEqual(0, FixedMath.Round(FixedMath.Create(0.1f))); + + // Keep in mind the current rounding uses 0.5 -> -1 + Assert.AreEqual(-1 << 16, FixedMath.Round(FixedMath.Create(-0.5f))); + Assert.AreEqual(-1 << 16, FixedMath.Round(FixedMath.Create(-0.6f))); + Assert.AreEqual(0, FixedMath.Round(FixedMath.Create(-0.4f))); + Assert.AreEqual(0, FixedMath.Round(FixedMath.Create(-0.1f))); + } + + [Test] + public void CreateFromFractionTest() { + Assert.AreEqual(0, FixedMath.Create(0, 100)); + Assert.AreEqual(1 << 15, FixedMath.Create(1, 2)); + Assert.AreEqual(-1 << 15, FixedMath.Create(-1, 2)); + Assert.AreEqual(-1 << 15, FixedMath.Create(1, -2)); + Assert.AreEqual(1 << 15, FixedMath.Create(-1, -2)); + } + + [Test] + public void MulDivTest() { + long two = FixedMath.Create(2); + // Assert 2x2 = 4 + Assert.AreEqual(1 << 18, two.Mul(two)); + + // Assert 2/2 = 1 + Assert.AreEqual(1 << 16, two.Div(two)); + + long max = FixedMath.Create(int.MaxValue); + long answer = FixedMath.Create(1073741823.5); + Assert.AreEqual(70368744144896L, answer); + + Assert.AreEqual(answer, max.Div(two)); + Assert.AreEqual(1 << 16, max.Div(max)); + } + + [Test] + public void ToIntTest() { + Assert.AreEqual(0, FixedMath.Create(0).ToInt()); + Assert.AreEqual(1, FixedMath.Create(1).ToInt()); + Assert.AreEqual(-1, FixedMath.Create(-1).ToInt()); + } + } +} + diff --git a/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs.meta b/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs.meta new file mode 100644 index 00000000..4f8366bc --- /dev/null +++ b/Core/Simulation/Math/Tests/Editor/FixedMathTest.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8293726fbe2e445eaa66b1e04a99e6dd +timeCreated: 1468530563 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: