From a1800e20f1c2087d749a504e569783839f4fb54c Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 13:26:51 -0700 Subject: [PATCH 01/14] Fixing default namespaces and references --- Tests/MathUnitTests/MathUnitTests.nfproj | 13 ++-- Tests/MathUnitTests/nano.runsettings | 17 ++++ .../MathBenchmark.cs | 78 +++++++++---------- ...nanoFramework.System.Math.Benchmark.nfproj | 30 +++---- .../nanoFramework.System.Math.nfproj | 8 +- 5 files changed, 80 insertions(+), 66 deletions(-) create mode 100644 Tests/MathUnitTests/nano.runsettings diff --git a/Tests/MathUnitTests/MathUnitTests.nfproj b/Tests/MathUnitTests/MathUnitTests.nfproj index 2e7ba19..1c0a799 100644 --- a/Tests/MathUnitTests/MathUnitTests.nfproj +++ b/Tests/MathUnitTests/MathUnitTests.nfproj @@ -30,20 +30,21 @@ - + + + + ..\..\packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll - True - + ..\..\packages\nanoFramework.TestFramework.2.1.85\lib\nanoFramework.TestFramework.dll - True - + ..\..\packages\nanoFramework.TestFramework.2.1.85\lib\nanoFramework.UnitTestLauncher.exe - True + diff --git a/Tests/MathUnitTests/nano.runsettings b/Tests/MathUnitTests/nano.runsettings new file mode 100644 index 0000000..93ce85e --- /dev/null +++ b/Tests/MathUnitTests/nano.runsettings @@ -0,0 +1,17 @@ + + + + + .\TestResults + 120000 + net48 + x64 + + + None + False + COM3 + + + + \ No newline at end of file diff --git a/nanoFramework.System.Math.Benchmark/MathBenchmark.cs b/nanoFramework.System.Math.Benchmark/MathBenchmark.cs index 723a114..fa49ac8 100644 --- a/nanoFramework.System.Math.Benchmark/MathBenchmark.cs +++ b/nanoFramework.System.Math.Benchmark/MathBenchmark.cs @@ -1,9 +1,9 @@ -extern alias Core; +using System; +using System.Diagnostics; using nanoFramework.Benchmark; using nanoFramework.Benchmark.Attributes; -using System.Diagnostics; -namespace nanoFramework.System.Math.Benchmark +namespace MathBenchmarks { [DebugLogger] [ConsoleParser] @@ -18,182 +18,182 @@ public MathBenchmark() [Benchmark] public void Math_Abs_int() { - var test = Core.System.Math.Abs(-15); + var test = Math.Abs(-15); } [Benchmark] public void Math_Abs_float() { - var test = Core.System.Math.Abs(-15f); + var test = Math.Abs(-15f); } [Benchmark] public void Math_Abs_double() { - var test = Core.System.Math.Abs(-15d); + var test = Math.Abs(-15d); } [Benchmark] public void Math_Acos() { - var test = Core.System.Math.Acos(-15); + var test = Math.Acos(-15); } [Benchmark] public void Math_Asin() { - var test = Core.System.Math.Asin(-15); + var test = Math.Asin(-15); } [Benchmark] public void Math_Atan() { - var test = Core.System.Math.Atan(-15); + var test = Math.Atan(-15); } [Benchmark] public void Math_Atan2() { - var test = Core.System.Math.Atan2(-15, -15); + var test = Math.Atan2(-15, -15); } [Benchmark] public void Math_Cbrt() { - var test = Core.System.Math.Cbrt(144); + var test = Math.Cbrt(144); } [Benchmark] public void Math_Ceiling() { - var test = Core.System.Math.Ceiling(-15); + var test = Math.Ceiling(-15); } [Benchmark] public void Math_Clamp_double() { - var test = Core.System.Math.Clamp(15d, -15d, 150d); + var test = Math.Clamp(15d, -15d, 150d); } [Benchmark] public void Math_Clamp_long() { - var test = Core.System.Math.Clamp(15L, -15L, 150L); + var test = Math.Clamp(15L, -15L, 150L); } [Benchmark] public void Math_Clamp_long_usigned() { - var test = Core.System.Math.Clamp(15u, -15u, 150u); + var test = Math.Clamp(15u, -15u, 150u); } [Benchmark] public void Math_Clamp_float() { - var test = Core.System.Math.Clamp(15f, -15f, 150f); + var test = Math.Clamp(15f, -15f, 150f); } [Benchmark] public void Math_Cos() { - var test = Core.System.Math.Cos(-15); + var test = Math.Cos(-15); } [Benchmark] public void Math_Cosh() { - var test = Core.System.Math.Cosh(-15); + var test = Math.Cosh(-15); } [Benchmark] public void Math_Exp() { - var test = Core.System.Math.Exp(-15); + var test = Math.Exp(-15); } [Benchmark] public void Math_Floor() { - var test = Core.System.Math.Floor(-15); + var test = Math.Floor(-15); } [Benchmark] public void Math_IEEERemainder() { - var test = Core.System.Math.IEEERemainder(-15, 15); + var test = Math.IEEERemainder(-15, 15); } [Benchmark] public void Math_Log() { - var test = Core.System.Math.Log(-15); + var test = Math.Log(-15); } [Benchmark] public void Math_Log10() { - var test = Core.System.Math.Log10(-15); + var test = Math.Log10(-15); } [Benchmark] public void Math_Max_int() { - var test = Core.System.Math.Max(-15, 15); + var test = Math.Max(-15, 15); } [Benchmark] public void Math_Max_double() { - var test = Core.System.Math.Max(-15d, 15d); + var test = Math.Max(-15d, 15d); } [Benchmark] public void Math_Max_float() { - var test = Core.System.Math.Max(-15f, 15f); + var test = Math.Max(-15f, 15f); } [Benchmark] public void Math_Min_int() { - var test = Core.System.Math.Min(-15, 15); + var test = Math.Min(-15, 15); } [Benchmark] public void Math_Min_double() { - var test = Core.System.Math.Min(-15d, 15d); + var test = Math.Min(-15d, 15d); } [Benchmark] public void Math_Min_float() { - var test = Core.System.Math.Min(-15f, 15f); + var test = Math.Min(-15f, 15f); } [Benchmark] public void Math_Pow() { - var test = Core.System.Math.Pow(-15, 15); + var test = Math.Pow(-15, 15); } [Benchmark] public void Math_Round() { - var test = Core.System.Math.Round(-15); + var test = Math.Round(-15); } [Benchmark] public void Math_Sign_double() { - var test = Core.System.Math.Sign(-15d); + var test = Math.Sign(-15d); } [Benchmark] public void Math_Sign_float() { - var test = Core.System.Math.Sign(-15f); + var test = Math.Sign(-15f); } [Benchmark] public void Math_Sin() { - var test = Core.System.Math.Sin(-15); + var test = Math.Sin(-15); } [Benchmark] public void Math_Sinh() { - var test = Core.System.Math.Sinh(-15); + var test = Math.Sinh(-15); } [Benchmark] public void Math_Sqrt() { - var test = Core.System.Math.Sqrt(-15); + var test = Math.Sqrt(-15); } [Benchmark] public void Math_Tan() { - var test = Core.System.Math.Tan(-15); + var test = Math.Tan(-15); } [Benchmark] public void Math_Tanh() { - var test = Core.System.Math.Tanh(-15); + var test = Math.Tanh(-15); } [Benchmark] public void Math_Truncate() { - var test = Core.System.Math.Truncate(-15); + var test = Math.Truncate(-15); } } } diff --git a/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj b/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj index 698fd41..1b30461 100644 --- a/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj +++ b/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj @@ -12,7 +12,7 @@ Exe Properties 512 - nanoFramework.System.Math.Benchmark + MathBenchmarks nanoFramework.System.Math.Benchmark v1.0 true @@ -26,42 +26,36 @@ - + + + + ..\packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll - True - + ..\packages\nanoFramework.Benchmark.1.0.56\lib\nanoFramework.Benchmark.dll - True - + ..\packages\nanoFramework.Logging.1.1.63\lib\nanoFramework.Logging.dll - True - + ..\packages\nanoFramework.Runtime.Native.1.6.6\lib\nanoFramework.Runtime.Native.dll - True - + ..\packages\nanoFramework.System.Collections.1.5.18\lib\nanoFramework.System.Collections.dll - True - + ..\packages\nanoFramework.System.Text.1.2.37\lib\nanoFramework.System.Text.dll - True - + ..\packages\nanoFramework.System.Diagnostics.Stopwatch.1.2.325\lib\System.Diagnostics.Stopwatch.dll - True - - Core - + diff --git a/nanoFramework.System.Math/nanoFramework.System.Math.nfproj b/nanoFramework.System.Math/nanoFramework.System.Math.nfproj index 59afdf6..1e3becd 100644 --- a/nanoFramework.System.Math/nanoFramework.System.Math.nfproj +++ b/nanoFramework.System.Math/nanoFramework.System.Math.nfproj @@ -13,7 +13,7 @@ Library Properties 512 - Sytem + System System.Math v1.0 True @@ -51,9 +51,11 @@ - + + + + ..\packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll - True From 4ebdc35a4e370fce71d9d22c295aa3447877f261 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 20:01:30 -0700 Subject: [PATCH 02/14] Fixing unit test warnings --- Tests/MathUnitTests/MathUnitTest.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/MathUnitTests/MathUnitTest.cs b/Tests/MathUnitTests/MathUnitTest.cs index e05b594..149c5cd 100644 --- a/Tests/MathUnitTests/MathUnitTest.cs +++ b/Tests/MathUnitTests/MathUnitTest.cs @@ -148,20 +148,20 @@ public static void Clamp_Float() [TestMethod] public static void Clamp_MinGreaterThanMax_ThrowsArgumentException() { - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((sbyte)1, (sbyte)2, (sbyte)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((byte)1, (byte)2, (byte)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((short)1, (short)2, (short)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((ushort)1, (ushort)2, (ushort)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((sbyte)1, (sbyte)2, (sbyte)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((byte)1, (byte)2, (byte)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((short)1, (short)2, (short)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((ushort)1, (ushort)2, (ushort)1)); // keeping cast on purpose to be able to test the method #pragma warning disable IDE0004 #pragma warning disable S1905 - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((int)1, (int)2, (int)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((uint)1, (uint)2, (uint)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((long)1, (long)2, (long)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((ulong)1, (ulong)2, (ulong)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((float)1, (float)2, (float)1)); - Assert.Throws(typeof(ArgumentException), () => Math.Clamp((double)1, (double)2, (double)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((int)1, (int)2, (int)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((uint)1, (uint)2, (uint)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((long)1, (long)2, (long)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((ulong)1, (ulong)2, (ulong)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((float)1, (float)2, (float)1)); + Assert.ThrowsException(typeof(ArgumentException), () => Math.Clamp((double)1, (double)2, (double)1)); #pragma warning restore S1905 #pragma warning restore IDE0004 } From 5d5adffb73b47db24608956ff81d9512905f0e37 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 20:01:39 -0700 Subject: [PATCH 03/14] Fixing namespace --- nanoFramework.System.Math.Benchmark/IAssemblyHandler.cs | 2 +- nanoFramework.System.Math.Benchmark/Program.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/nanoFramework.System.Math.Benchmark/IAssemblyHandler.cs b/nanoFramework.System.Math.Benchmark/IAssemblyHandler.cs index e7ddbc6..07ccd5e 100644 --- a/nanoFramework.System.Math.Benchmark/IAssemblyHandler.cs +++ b/nanoFramework.System.Math.Benchmark/IAssemblyHandler.cs @@ -1,4 +1,4 @@ -namespace nanoFramework.System.Math.Benchmark +namespace MathBenchmarks { public interface IAssemblyHandler { diff --git a/nanoFramework.System.Math.Benchmark/Program.cs b/nanoFramework.System.Math.Benchmark/Program.cs index f394e8d..14899dd 100644 --- a/nanoFramework.System.Math.Benchmark/Program.cs +++ b/nanoFramework.System.Math.Benchmark/Program.cs @@ -1,9 +1,7 @@ -using nanoFramework.Benchmark; -using System; -using System.Diagnostics; using System.Threading; +using nanoFramework.Benchmark; -namespace nanoFramework.System.Math.Benchmark +namespace MathBenchmarks { public class Program { From 697f8ab01ed53ef04c2779922747bee591869861 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 20:02:06 -0700 Subject: [PATCH 04/14] Adding unit tests --- Tests/MathUnitTests/MathUnitTests.nfproj | 2 + Tests/MathUnitTests/Math_Max_Tests.cs | 74 ++++++++++++++++++++ Tests/MathUnitTests/Math_Min_Tests.cs | 87 ++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 Tests/MathUnitTests/Math_Max_Tests.cs create mode 100644 Tests/MathUnitTests/Math_Min_Tests.cs diff --git a/Tests/MathUnitTests/MathUnitTests.nfproj b/Tests/MathUnitTests/MathUnitTests.nfproj index 1c0a799..11db493 100644 --- a/Tests/MathUnitTests/MathUnitTests.nfproj +++ b/Tests/MathUnitTests/MathUnitTests.nfproj @@ -27,6 +27,8 @@ + + diff --git a/Tests/MathUnitTests/Math_Max_Tests.cs b/Tests/MathUnitTests/Math_Max_Tests.cs new file mode 100644 index 0000000..266ab32 --- /dev/null +++ b/Tests/MathUnitTests/Math_Max_Tests.cs @@ -0,0 +1,74 @@ +using System; +using nanoFramework.TestFramework; + +namespace MathUnitTests +{ + [TestClass] + public class Math_Max_Tests + { + [TestMethod] + public void Max_Double_returns_greater_value() + { + // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned + Assert.SkipTest("Skipping pending further investigation"); + + var expect = 1234.5678d; + var lower = expect / 2; + + Assert.AreEqual(expect, Math.Max(expect, lower)); + Assert.AreEqual(expect, Math.Max(lower, expect)); + } + + [TestMethod] + public void Max_Double_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Max(double.NaN, Math.PI); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Max_Double_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Max(Math.PI, double.NaN); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Max_Float_returns_greater_value() + { + var expect = 1234.5678f; + var lower = expect / 2; + + Assert.AreEqual(expect, Math.Max(expect, lower)); + Assert.AreEqual(expect, Math.Max(lower, expect)); + } + + [TestMethod] + public void Max_Float_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Max(float.NaN, (float)Math.PI); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Max_Float_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Max((float)Math.PI, float.NaN); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Max_Int_returns_greater_value() + { + var expect = 12345678; + var lower = expect / 2; + + Assert.AreEqual(expect, Math.Max(expect, lower)); + Assert.AreEqual(expect, Math.Max(lower, expect)); + } + } +} diff --git a/Tests/MathUnitTests/Math_Min_Tests.cs b/Tests/MathUnitTests/Math_Min_Tests.cs new file mode 100644 index 0000000..c587379 --- /dev/null +++ b/Tests/MathUnitTests/Math_Min_Tests.cs @@ -0,0 +1,87 @@ +using System; +using nanoFramework.TestFramework; + +namespace MathUnitTests +{ + [TestClass] + public class Math_Min_Tests + { + [TestMethod] + public void Min_Double_returns_lesser_value() + { + // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned + Assert.SkipTest("Skipping pending further investigation"); + + var expect = 1234.5678d; + var higher = expect * 2.0d; + + /* + var actual1 = Math.Min(expect, higher); + var actual2 = Math.Min(expect, higher); + + Console.WriteLine($"Test: {Math.Abs(expect - actual1)}"); + Console.WriteLine($"Double: {double.Epsilon}"); + Console.WriteLine($"Float: {float.Epsilon}"); + + Assert.IsTrue(Math.Abs(expect - actual1) < double.Epsilon); + Assert.IsTrue(Math.Abs(expect - actual2) < double.Epsilon); + */ + + Assert.AreEqual(expect, Math.Min(expect, higher)); + Assert.AreEqual(expect, Math.Min(higher, expect)); + + } + + [TestMethod] + public void Min_Double_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Min(double.NaN, Math.PI); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Min_Double_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Min(Math.PI, double.NaN); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Min_Float_returns_lesser_value() + { + var expect = 1234.5678f; + var higher = expect * 2.0f; + + Assert.AreEqual(expect, Math.Min(expect, higher)); + Assert.AreEqual(expect, Math.Min(higher, expect)); + } + + [TestMethod] + public void Min_Float_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Min(float.NaN, (float) Math.PI); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Min_Float_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Min((float) Math.PI, float.NaN); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Min_Int_returns_lesser_value() + { + var expect = 12345678; + var higher = expect * 2; + + Assert.AreEqual(expect, Math.Min(expect, higher)); + Assert.AreEqual(expect, Math.Min(higher, expect)); + } + } +} From 3cc202d6ea7b50a1933ececd02a442e8c5d91816 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 20:17:27 -0700 Subject: [PATCH 05/14] Adding benchmarks --- .../BenchmarksBase.cs | 18 +++ .../MaxBenchmarks.cs | 56 +++++++++ .../MinBenchmarks.cs | 109 ++++++++++++++++++ .../Program.cs | 15 ++- ...nanoFramework.System.Math.Benchmark.nfproj | 3 + 5 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 nanoFramework.System.Math.Benchmark/BenchmarksBase.cs create mode 100644 nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs create mode 100644 nanoFramework.System.Math.Benchmark/MinBenchmarks.cs diff --git a/nanoFramework.System.Math.Benchmark/BenchmarksBase.cs b/nanoFramework.System.Math.Benchmark/BenchmarksBase.cs new file mode 100644 index 0000000..ea6b2b9 --- /dev/null +++ b/nanoFramework.System.Math.Benchmark/BenchmarksBase.cs @@ -0,0 +1,18 @@ +using System; + +namespace MathBenchmarks +{ + public abstract class BenchmarksBase + { + protected const int Iterations = 250; + protected const int Loops = 100; + + public void RunIterations(int iterations, Action action) + { + for (var i = 0; i < iterations; i++) + { + action(); + } + } + } +} diff --git a/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs b/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs new file mode 100644 index 0000000..0c5f87f --- /dev/null +++ b/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs @@ -0,0 +1,56 @@ +using nanoFramework.Benchmark; +using nanoFramework.Benchmark.Attributes; +using System; + +namespace MathBenchmarks +{ + [IterationCount(Iterations)] + public class MaxBenchmarks: BenchmarksBase + { + protected const double DoublePositive1 = Math.PI; + protected const double DoublePositive2 = DoublePositive1 * 2; + protected const double DoubleNegative1 = DoublePositive1 * -1; + + protected const float FloatPositive1 = (float)Math.PI; + protected const float FloatPositive2 = FloatPositive1 * 2; + protected const float FloatNegative1 = FloatPositive1 * -1; + + protected const int IntPositive1 = 123456789; + protected const int IntPositive2 = IntPositive1 * 2; + protected const int IntNegative1 = IntPositive1 * -1; + + [Benchmark] + public void Max_Double() + { + RunIterations(Loops, () => + { + var result1 = Math.Max(DoublePositive1, DoubleNegative1); + var result2 = Math.Max(DoublePositive2, DoublePositive1); + var result3 = Math.Max(double.NaN, DoublePositive2); + }); + } + + // TODO: This is much slower than System.Math.Max(double,double) and I'm assuming it's because the implementation is a cast to the native double method + [Benchmark] + public void Max_Float() + { + RunIterations(Loops, () => + { + var result1 = Math.Max(FloatPositive1, FloatNegative1); + var result2 = Math.Max(FloatPositive2, FloatPositive1); + var result3 = Math.Max(float.NaN, FloatPositive2); + }); + } + + [Benchmark] + public void Max_Int() + { + RunIterations(Loops, () => + { + var result1 = Math.Max(IntPositive1, IntNegative1); + var result2 = Math.Max(IntPositive2, IntPositive1); + var result3 = Math.Max(IntNegative1, IntPositive2); + }); + } + } +} diff --git a/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs new file mode 100644 index 0000000..d83f1ea --- /dev/null +++ b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs @@ -0,0 +1,109 @@ +using nanoFramework.Benchmark; +using nanoFramework.Benchmark.Attributes; + +namespace MathBenchmarks +{ + [IterationCount(Iterations)] + public class MinBenchmarks: BenchmarksBase + { + [Benchmark] + public void Min_Double_Fast() + { + RunIterations(Loops, () => + { + var result1 = FastMath.Min(DoublePositive1, DoubleNegative1); + var result2 = FastMath.Min(DoublePositive2, DoublePositive1); + var result3 = FastMath.Min(double.NaN, DoublePositive2); + }); + } + + [Benchmark] + public void Min_Double_Math() + { + RunIterations(Loops, () => + { + var result1 = Math.Min(DoublePositive1, DoubleNegative1); + var result2 = Math.Min(DoublePositive2, DoublePositive1); + var result3 = Math.Min(double.NaN, DoublePositive2); + }); + } + + [Benchmark] + public void Min_Double_System() + { + RunIterations(Loops, () => + { + var result1 = System.Math.Min(DoublePositive1, DoubleNegative1); + var result2 = System.Math.Min(DoublePositive2, DoublePositive1); + var result3 = System.Math.Min(double.NaN, DoublePositive2); + }); + } + + [Benchmark] + public void Min_Float_Fast() + { + RunIterations(Loops, () => + { + var result1 = FastMath.Min(FloatPositive1, FloatNegative1); + var result2 = FastMath.Min(FloatPositive2, FloatPositive1); + var result3 = FastMath.Min(float.NaN, FloatPositive2); + }); + } + + [Benchmark] + public void Min_Float_Math() + { + RunIterations(Loops, () => + { + var result1 = Math.Min(FloatPositive1, FloatNegative1); + var result2 = Math.Min(FloatPositive2, FloatPositive1); + var result3 = Math.Min(float.NaN, FloatPositive2); + }); + } + + // TODO: This is much slower than System.Math.Min(double,double) and I'm assuming it's because the implementation is a cast to the native double method + [Benchmark] + public void Min_Float_System() + { + RunIterations(Loops, () => + { + var result1 = System.Math.Min(FloatPositive1, FloatNegative1); + var result2 = System.Math.Min(FloatPositive2, FloatPositive1); + var result3 = System.Math.Min(float.NaN, FloatPositive2); + }); + } + + [Benchmark] + public void Min_Int_Fast() + { + RunIterations(Loops, () => + { + var result1 = FastMath.Min(IntPositive1, IntNegative1); + var result2 = FastMath.Min(IntPositive2, IntPositive1); + var result3 = FastMath.Min(IntNegative1, IntPositive2); + }); + } + + [Benchmark] + public void Min_Int_Math() + { + RunIterations(Loops, () => + { + var result1 = Math.Min(IntPositive1, IntNegative1); + var result2 = Math.Min(IntPositive2, IntPositive1); + var result3 = Math.Min(IntNegative1, IntPositive2); + }); + } + + [Benchmark] + public void Min_Int_System() + { + RunIterations(Loops, () => + { + var result1 = System.Math.Min(IntPositive1, IntNegative1); + var result2 = System.Math.Min(IntPositive2, IntPositive1); + var result3 = System.Math.Min(IntNegative1, IntPositive2); + }); + } + } +} diff --git a/nanoFramework.System.Math.Benchmark/Program.cs b/nanoFramework.System.Math.Benchmark/Program.cs index 14899dd..6a69f5d 100644 --- a/nanoFramework.System.Math.Benchmark/Program.cs +++ b/nanoFramework.System.Math.Benchmark/Program.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; +using System; using System.Threading; using nanoFramework.Benchmark; @@ -7,7 +9,18 @@ public class Program { public static void Main() { - BenchmarkRunner.Run(typeof(IAssemblyHandler).Assembly); +#if DEBUG + Console.WriteLine("Benchmarks should be run in a release build."); + Debugger.Break(); + return; +#endif + Console.WriteLine("Running benchmarks..."); + + //BenchmarkRunner.Run(typeof(IAssemblyHandler).Assembly); + + BenchmarkRunner.RunClass(typeof(MaxBenchmarks)); + BenchmarkRunner.RunClass(typeof(MinBenchmarks)); + Thread.Sleep(Timeout.Infinite); } } diff --git a/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj b/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj index 1b30461..4210617 100644 --- a/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj +++ b/nanoFramework.System.Math.Benchmark/nanoFramework.System.Math.Benchmark.nfproj @@ -20,8 +20,11 @@ + + + From b603c589cc5e6352b48a496aef1f2d4f44ae4401 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 20:21:09 -0700 Subject: [PATCH 06/14] Forgot to fix this one before commiting --- .../MinBenchmarks.cs | 85 ++++--------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs index d83f1ea..a4a553d 100644 --- a/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs +++ b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs @@ -1,24 +1,26 @@ using nanoFramework.Benchmark; using nanoFramework.Benchmark.Attributes; +using System; namespace MathBenchmarks { [IterationCount(Iterations)] public class MinBenchmarks: BenchmarksBase { - [Benchmark] - public void Min_Double_Fast() - { - RunIterations(Loops, () => - { - var result1 = FastMath.Min(DoublePositive1, DoubleNegative1); - var result2 = FastMath.Min(DoublePositive2, DoublePositive1); - var result3 = FastMath.Min(double.NaN, DoublePositive2); - }); - } + protected const double DoublePositive1 = Math.PI; + protected const double DoublePositive2 = DoublePositive1 * 2; + protected const double DoubleNegative1 = DoublePositive1 * -1; + + protected const float FloatPositive1 = (float)Math.PI; + protected const float FloatPositive2 = FloatPositive1 * 2; + protected const float FloatNegative1 = FloatPositive1 * -1; + + protected const int IntPositive1 = 123456789; + protected const int IntPositive2 = IntPositive1 * 2; + protected const int IntNegative1 = IntPositive1 * -1; [Benchmark] - public void Min_Double_Math() + public void Min_Double() { RunIterations(Loops, () => { @@ -28,30 +30,9 @@ public void Min_Double_Math() }); } + // TODO: This is much slower than System.Math.Max(double,double) and I'm assuming it's because the implementation is a cast to the native double method [Benchmark] - public void Min_Double_System() - { - RunIterations(Loops, () => - { - var result1 = System.Math.Min(DoublePositive1, DoubleNegative1); - var result2 = System.Math.Min(DoublePositive2, DoublePositive1); - var result3 = System.Math.Min(double.NaN, DoublePositive2); - }); - } - - [Benchmark] - public void Min_Float_Fast() - { - RunIterations(Loops, () => - { - var result1 = FastMath.Min(FloatPositive1, FloatNegative1); - var result2 = FastMath.Min(FloatPositive2, FloatPositive1); - var result3 = FastMath.Min(float.NaN, FloatPositive2); - }); - } - - [Benchmark] - public void Min_Float_Math() + public void Min_Float() { RunIterations(Loops, () => { @@ -61,31 +42,8 @@ public void Min_Float_Math() }); } - // TODO: This is much slower than System.Math.Min(double,double) and I'm assuming it's because the implementation is a cast to the native double method [Benchmark] - public void Min_Float_System() - { - RunIterations(Loops, () => - { - var result1 = System.Math.Min(FloatPositive1, FloatNegative1); - var result2 = System.Math.Min(FloatPositive2, FloatPositive1); - var result3 = System.Math.Min(float.NaN, FloatPositive2); - }); - } - - [Benchmark] - public void Min_Int_Fast() - { - RunIterations(Loops, () => - { - var result1 = FastMath.Min(IntPositive1, IntNegative1); - var result2 = FastMath.Min(IntPositive2, IntPositive1); - var result3 = FastMath.Min(IntNegative1, IntPositive2); - }); - } - - [Benchmark] - public void Min_Int_Math() + public void Min_Int() { RunIterations(Loops, () => { @@ -94,16 +52,5 @@ public void Min_Int_Math() var result3 = Math.Min(IntNegative1, IntPositive2); }); } - - [Benchmark] - public void Min_Int_System() - { - RunIterations(Loops, () => - { - var result1 = System.Math.Min(IntPositive1, IntNegative1); - var result2 = System.Math.Min(IntPositive2, IntPositive1); - var result3 = System.Math.Min(IntNegative1, IntPositive2); - }); - } } } From 74106762e97ff3eb93c4b7f28d8f5bc28573f215 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 22:28:41 -0700 Subject: [PATCH 07/14] Moving Max(float) and Min(float) to native --- Tests/MathUnitTests/MathUnitTest.cs | 2 +- nanoFramework.System.Math/Math.cs | 12 ++++-------- nanoFramework.System.Math/Properties/AssemblyInfo.cs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Tests/MathUnitTests/MathUnitTest.cs b/Tests/MathUnitTests/MathUnitTest.cs index 149c5cd..6b5da37 100644 --- a/Tests/MathUnitTests/MathUnitTest.cs +++ b/Tests/MathUnitTests/MathUnitTest.cs @@ -739,7 +739,7 @@ public static void Test_Min_2() Assert.IsTrue(double.IsNaN(res), $"Min(...10,double.NaN) -- FAILED AT: {res}"); res = Math.Min(double.NaN, 10); - Assert.AreEqual(res, 10, $"Min(...NaN, 10) -- FAILED AT: {res}"); + Assert.AreEqual(double.IsNaN(res), 10, $"Min(...NaN, 10) -- FAILED AT: {res}"); res = Math.Min(double.NaN, double.NaN); Assert.IsTrue(double.IsNaN(res), $"Min(...NaN,double.NaN) -- FAILED AT: {res}"); diff --git a/nanoFramework.System.Math/Math.cs b/nanoFramework.System.Math/Math.cs index 7bf2ba8..13af0c3 100644 --- a/nanoFramework.System.Math/Math.cs +++ b/nanoFramework.System.Math/Math.cs @@ -373,10 +373,8 @@ public static int Max(int val1, int val2) /// The first of two single-precision floating-point numbers to compare. /// The second of two single-precision floating-point numbers to compare. /// Parameter or , whichever is larger. If , or , or both and are equal to , is returned. - public static float Max(float val1, float val2) - { - return (float)Max((double)val1, val2); - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Max(float val1, float val2); /// /// Returns the smaller of two 32-bit signed integers. @@ -406,10 +404,8 @@ public static int Min(int val1, int val2) /// The first of two single-precision floating-point numbers to compare. /// The second of two single-precision floating-point numbers to compare. /// Parameter or , whichever is smaller. If , , or both and are equal to , is returned. - public static float Min(float val1, float val2) - { - return (float)Min((double)val1, val2); - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Min(float val1, float val2); /// /// Returns a specified number raised to the specified power. diff --git a/nanoFramework.System.Math/Properties/AssemblyInfo.cs b/nanoFramework.System.Math/Properties/AssemblyInfo.cs index b20857c..bc927f5 100644 --- a/nanoFramework.System.Math/Properties/AssemblyInfo.cs +++ b/nanoFramework.System.Math/Properties/AssemblyInfo.cs @@ -12,6 +12,6 @@ //////////////////////////////////////////////////////////////// // update this whenever the native assembly signature changes // -[assembly: AssemblyNativeVersion("100.0.5.4")] +[assembly: AssemblyNativeVersion("100.0.5.5")] //////////////////////////////////////////////////////////////// From 79c092d64088c46797f10ba76f3b789884f02a60 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 4 Nov 2023 23:49:02 -0700 Subject: [PATCH 08/14] Cleaning up unit tests --- Tests/MathUnitTests/MathUnitTest.cs | 80 ----------- Tests/MathUnitTests/MathUnitTests.nfproj | 4 +- Tests/MathUnitTests/Math_Max_Tests.cs | 74 ---------- Tests/MathUnitTests/Max_Tests.cs | 132 ++++++++++++++++++ .../{Math_Min_Tests.cs => Min_Tests.cs} | 70 +++++++++- 5 files changed, 198 insertions(+), 162 deletions(-) delete mode 100644 Tests/MathUnitTests/Math_Max_Tests.cs create mode 100644 Tests/MathUnitTests/Max_Tests.cs rename Tests/MathUnitTests/{Math_Min_Tests.cs => Min_Tests.cs} (50%) diff --git a/Tests/MathUnitTests/MathUnitTest.cs b/Tests/MathUnitTests/MathUnitTest.cs index 6b5da37..71d25b1 100644 --- a/Tests/MathUnitTests/MathUnitTest.cs +++ b/Tests/MathUnitTests/MathUnitTest.cs @@ -665,86 +665,6 @@ public static void Test_Log10() Assert.IsTrue(double.IsNaN(res), $"Log10(...NegativeInfinity) -- FAILED AT: {res}"); } - [TestMethod] - public static void Test_Max_2() - { - // - // Summary: - // Returns the larger of two double-precision floating-point numbers. - // - // Parameters: - // val1: - // The first of two double-precision floating-point numbers to compare. - // - // val2: - // The second of two double-precision floating-point numbers to compare. - // - // Returns: - // Parameter val1 or val2, whichever is larger. If val1 OR both val1 - // and val2 are equal to System.Double.NaN, System.Double.NaN is returned. - double[] x = new double[] { 6.00000000000000000000, 6.50000000000000000000, 7.00000000000000000000, 7.50000000000000000000, -0.50000000000000000000, -1.00000000000000000000, -1.50000000000000000000, -2.00000000000000000000 }; - double[] y = new double[] { 1, 1.140238321, 1.600286858, 2.509178479, 4.121836054, 6.890572365, 11.59195328, -0.50000000000000000000 }; - - double[] answer = new double[] { 6.00000000000000000000, 6.50000000000000000000, 7.00000000000000000000, 7.50000000000000000000, 4.12183605386995000000, 6.89057236497588000000, 11.59195328, -0.50000000000000000000 }; - double res; - - for (int i = 0; i < x.Length; i++) - { - res = Math.Max(x[i], y[i]); - - Assert.IsFalse((answer[i] - res) > 0.0001d || (answer[i] - res) < -0.0001d, $"Max(...{x[i]}, {y[i]}) -- FAILED AT: {res}"); - } - - res = Math.Max(10, double.NaN); - Assert.IsTrue(double.IsNaN(res), $"Max(...10,double.NaN) -- FAILED AT: {res}"); - - res = Math.Max(double.NaN, 10); - Assert.IsTrue(double.IsNaN(res), $"Max(...NaN, 10) -- FAILED AT: {res}"); - - res = Math.Max(double.NaN, double.NaN); - Assert.IsTrue(double.IsNaN(res), $"Max(...NaN,double.NaN) -- FAILED AT: {res}"); - } - - [TestMethod] - public static void Test_Min_2() - { - // - // Summary: - // Returns the smaller of two double-precision floating-point numbers. - // - // Parameters: - // val1: - // The first of two double-precision floating-point numbers to compare. - // - // val2: - // The second of two double-precision floating-point numbers to compare. - // - // Returns: - // Parameter val1 or val2, whichever is smaller. If val1, val2, or both val1 - // and val2 are equal to System.Double.NaN, System.Double.NaN is returned. - double[] x = new double[] { 6.00000000000000000000, 6.50000000000000000000, 7.00000000000000000000, 7.50000000000000000000, -0.50000000000000000000, -1.00000000000000000000, -1.50000000000000000000, -2.00000000000000000000 }; - double[] y = new double[] { 1, 1.140238321, 1.600286858, 2.509178479, 4.121836054, 6.890572365, 11.59195328, -0.50000000000000000000 }; - - double[] answer = new double[] { 1, 1.140238321, 1.600286858, 2.509178479, -0.50000000000000000000, -1.00000000000000000000, -1.50000000000000000000, -2.00000000000000000000 }; - double res; - - for (int i = 0; i < x.Length; i++) - { - res = Math.Min(x[i], y[i]); - - Assert.IsFalse((answer[i] - res) > 0.0001d || (answer[i] - res) < -0.0001d, $"Min(...{x[i]}, {y[i]}) -- FAILED AT: {res}"); - } - - res = Math.Min(10, double.NaN); - Assert.IsTrue(double.IsNaN(res), $"Min(...10,double.NaN) -- FAILED AT: {res}"); - - res = Math.Min(double.NaN, 10); - Assert.AreEqual(double.IsNaN(res), 10, $"Min(...NaN, 10) -- FAILED AT: {res}"); - - res = Math.Min(double.NaN, double.NaN); - Assert.IsTrue(double.IsNaN(res), $"Min(...NaN,double.NaN) -- FAILED AT: {res}"); - } - [TestMethod] public static void Test_Pow_2() { diff --git a/Tests/MathUnitTests/MathUnitTests.nfproj b/Tests/MathUnitTests/MathUnitTests.nfproj index 11db493..2bdec42 100644 --- a/Tests/MathUnitTests/MathUnitTests.nfproj +++ b/Tests/MathUnitTests/MathUnitTests.nfproj @@ -27,8 +27,8 @@ - - + + diff --git a/Tests/MathUnitTests/Math_Max_Tests.cs b/Tests/MathUnitTests/Math_Max_Tests.cs deleted file mode 100644 index 266ab32..0000000 --- a/Tests/MathUnitTests/Math_Max_Tests.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using nanoFramework.TestFramework; - -namespace MathUnitTests -{ - [TestClass] - public class Math_Max_Tests - { - [TestMethod] - public void Max_Double_returns_greater_value() - { - // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned - Assert.SkipTest("Skipping pending further investigation"); - - var expect = 1234.5678d; - var lower = expect / 2; - - Assert.AreEqual(expect, Math.Max(expect, lower)); - Assert.AreEqual(expect, Math.Max(lower, expect)); - } - - [TestMethod] - public void Max_Double_returns_NaN_if_val1_is_NaN() - { - var actual = Math.Max(double.NaN, Math.PI); - - Assert.IsTrue(double.IsNaN(actual)); - } - - [TestMethod] - public void Max_Double_returns_NaN_if_val2_is_NaN() - { - var actual = Math.Max(Math.PI, double.NaN); - - Assert.IsTrue(double.IsNaN(actual)); - } - - [TestMethod] - public void Max_Float_returns_greater_value() - { - var expect = 1234.5678f; - var lower = expect / 2; - - Assert.AreEqual(expect, Math.Max(expect, lower)); - Assert.AreEqual(expect, Math.Max(lower, expect)); - } - - [TestMethod] - public void Max_Float_returns_NaN_if_val1_is_NaN() - { - var actual = Math.Max(float.NaN, (float)Math.PI); - - Assert.IsTrue(float.IsNaN(actual)); - } - - [TestMethod] - public void Max_Float_returns_NaN_if_val2_is_NaN() - { - var actual = Math.Max((float)Math.PI, float.NaN); - - Assert.IsTrue(float.IsNaN(actual)); - } - - [TestMethod] - public void Max_Int_returns_greater_value() - { - var expect = 12345678; - var lower = expect / 2; - - Assert.AreEqual(expect, Math.Max(expect, lower)); - Assert.AreEqual(expect, Math.Max(lower, expect)); - } - } -} diff --git a/Tests/MathUnitTests/Max_Tests.cs b/Tests/MathUnitTests/Max_Tests.cs new file mode 100644 index 0000000..4f391f7 --- /dev/null +++ b/Tests/MathUnitTests/Max_Tests.cs @@ -0,0 +1,132 @@ +using System; +using nanoFramework.TestFramework; + +namespace MathUnitTests +{ + [TestClass] + public class Max_Tests + { + [TestMethod] + public void Max_Double_returns_greater_value_INVESTIGATE() + { + // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned + Assert.SkipTest("Skipping pending further investigation"); + + var expect = 1234.5678d; + var lower = expect / 2; + + Assert.AreEqual(expect, Math.Max(expect, lower)); + Assert.AreEqual(expect, Math.Max(lower, expect)); + } + + [TestMethod] + public void Max_Double_returns_greater_value() + { + var val1 = new[] + { + 6.00000000000000000000d, 6.50000000000000000000d, 7.00000000000000000000d, 7.50000000000000000000d, + -0.50000000000000000000d, -1.00000000000000000000d, -1.50000000000000000000d, -2.00000000000000000000d + }; + var val2 = new[] + { + 1d, 1.140238321d, 1.600286858d, 2.509178479d, 4.121836054d, 6.890572365d, 11.59195328d, -0.50000000000000000000d + }; + + var expected = new[] + { + val1[0], val1[1], val1[2], val1[3], val2[4], val2[5], val2[6], val2[7] + }; + + for (var i = 0; i < val1.Length; i++) + { + var actual = Math.Max(val1[i], val2[i]); + + Assert.IsFalse((expected[i] - actual) > 0.0001d || (expected[i] - actual) < -0.0001d); + } + } + + [TestMethod] + public void Max_Double_returns_NaN_if_both_val1_and_val2_are_NaN() + { + var actual = Math.Max(double.NaN, double.NaN); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Max_Double_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Max(double.NaN, Math.PI); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Max_Double_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Max(Math.PI, double.NaN); + + Assert.IsTrue(double.IsNaN(actual)); + } + + [TestMethod] + public void Max_Float_returns_greater_value() + { + var val1 = new[] + { + 6.00000000000000000000f, 6.50000000000000000000f, 7.00000000000000000000f, 7.50000000000000000000f, + -0.50000000000000000000f, -1.00000000000000000000f, -1.50000000000000000000f, -2.00000000000000000000f + }; + var val2 = new[] + { + 1f, 1.140238321f, 1.600286858f, 2.509178479f, 4.121836054f, 6.890572365f, 11.59195328f, -0.50000000000000000000f + }; + + var expected = new[] + { + val1[0], val1[1], val1[2], val1[3], val2[4], val2[5], val2[6], val2[7] + }; + + for (var i = 0; i < val1.Length; i++) + { + var actual = Math.Max(val1[i], val2[i]); + + Assert.AreEqual(expected[i], actual); + } + } + + [TestMethod] + public void Max_Float_returns_NaN_if_both_val1_and_val2_are_NaN() + { + var actual = Math.Max(float.NaN, float.NaN); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Max_Float_returns_NaN_if_val1_is_NaN() + { + var actual = Math.Max(float.NaN, (float)Math.PI); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Max_Float_returns_NaN_if_val2_is_NaN() + { + var actual = Math.Max((float)Math.PI, float.NaN); + + Assert.IsTrue(float.IsNaN(actual)); + } + + [TestMethod] + public void Max_Int_returns_greater_value() + { + var expect = 12345678; + var lower = expect / 2; + + Assert.AreEqual(expect, Math.Max(expect, lower)); + Assert.AreEqual(expect, Math.Max(lower, expect)); + } + } +} diff --git a/Tests/MathUnitTests/Math_Min_Tests.cs b/Tests/MathUnitTests/Min_Tests.cs similarity index 50% rename from Tests/MathUnitTests/Math_Min_Tests.cs rename to Tests/MathUnitTests/Min_Tests.cs index c587379..26d4b6b 100644 --- a/Tests/MathUnitTests/Math_Min_Tests.cs +++ b/Tests/MathUnitTests/Min_Tests.cs @@ -4,10 +4,10 @@ namespace MathUnitTests { [TestClass] - public class Math_Min_Tests + public class Min_Tests { [TestMethod] - public void Min_Double_returns_lesser_value() + public void Min_Double_returns_lesser_value_INVESTIGATE() { // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned Assert.SkipTest("Skipping pending further investigation"); @@ -32,6 +32,40 @@ public void Min_Double_returns_lesser_value() } + [TestMethod] + public void Min_Double_returns_lesser_value() + { + var val1 = new[] + { + 6.00000000000000000000d, 6.50000000000000000000d, 7.00000000000000000000d, 7.50000000000000000000d, + -0.50000000000000000000d, -1.00000000000000000000d, -1.50000000000000000000d, -2.00000000000000000000d + }; + var val2 = new[] + { + 1d, 1.140238321d, 1.600286858d, 2.509178479d, 4.121836054d, 6.890572365d, 11.59195328d, -0.50000000000000000000d + }; + + var expected = new[] + { + val2[0], val2[1], val2[2], val2[3], val1[4], val1[5], val1[6], val1[7] + }; + + for (var i = 0; i < val1.Length; i++) + { + var actual = Math.Min(val1[i], val2[i]); + + Assert.IsFalse((expected[i] - actual) > 0.0001d || (expected[i] - actual) < -0.0001d); + } + } + + [TestMethod] + public void Min_Double_returns_NaN_if_both_val1_and_val2_are_NaN() + { + var actual = Math.Min(double.NaN, double.NaN); + + Assert.IsTrue(double.IsNaN(actual)); + } + [TestMethod] public void Min_Double_returns_NaN_if_val1_is_NaN() { @@ -51,11 +85,35 @@ public void Min_Double_returns_NaN_if_val2_is_NaN() [TestMethod] public void Min_Float_returns_lesser_value() { - var expect = 1234.5678f; - var higher = expect * 2.0f; + var val1 = new[] + { + 6.00000000000000000000f, 6.50000000000000000000f, 7.00000000000000000000f, 7.50000000000000000000f, + -0.50000000000000000000f, -1.00000000000000000000f, -1.50000000000000000000f, -2.00000000000000000000f + }; + var val2 = new[] + { + 1f, 1.140238321f, 1.600286858f, 2.509178479f, 4.121836054f, 6.890572365f, 11.59195328f, -0.50000000000000000000f + }; + + var expected = new[] + { + val2[0], val2[1], val2[2], val2[3], val1[4], val1[5], val1[6], val1[7] + }; + + for (var i = 0; i < val1.Length; i++) + { + var actual = Math.Min(val1[i], val2[i]); + + Assert.AreEqual(expected[i], actual); + } + } - Assert.AreEqual(expect, Math.Min(expect, higher)); - Assert.AreEqual(expect, Math.Min(higher, expect)); + [TestMethod] + public void Min_Float_returns_NaN_if_both_val1_and_val2_are_NaN() + { + var actual = Math.Min(float.NaN, float.NaN); + + Assert.IsTrue(float.IsNaN(actual)); } [TestMethod] From d516533550abf0a18f9789a75f15fe06f6d7d25c Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sun, 5 Nov 2023 01:19:03 -0700 Subject: [PATCH 09/14] Making Max(int) and Min(int) faster too --- nanoFramework.System.Math/Math.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nanoFramework.System.Math/Math.cs b/nanoFramework.System.Math/Math.cs index 13af0c3..cb6e35c 100644 --- a/nanoFramework.System.Math/Math.cs +++ b/nanoFramework.System.Math/Math.cs @@ -353,7 +353,7 @@ public static ulong Clamp( /// Parameter or , whichever is larger. public static int Max(int val1, int val2) { - return MathInternal.Max(val1, val2); + return (val1 >= val2) ? val1 : val2; } /// @@ -384,7 +384,7 @@ public static int Max(int val1, int val2) /// Parameter or , whichever is smaller. public static int Min(int val1, int val2) { - return MathInternal.Min(val1, val2); + return (val1 <= val2) ? val1 : val2; } /// From 6af25029ee0e1c51b0bd0b7f5a0aeb476da1a143 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sun, 5 Nov 2023 01:35:41 -0700 Subject: [PATCH 10/14] Removing notes in benchmarks --- nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs | 1 - nanoFramework.System.Math.Benchmark/MinBenchmarks.cs | 1 - nanoFramework.System.Math/Math.cs | 4 +++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs b/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs index 0c5f87f..2d0ee02 100644 --- a/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs +++ b/nanoFramework.System.Math.Benchmark/MaxBenchmarks.cs @@ -30,7 +30,6 @@ public void Max_Double() }); } - // TODO: This is much slower than System.Math.Max(double,double) and I'm assuming it's because the implementation is a cast to the native double method [Benchmark] public void Max_Float() { diff --git a/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs index a4a553d..a6ef46e 100644 --- a/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs +++ b/nanoFramework.System.Math.Benchmark/MinBenchmarks.cs @@ -30,7 +30,6 @@ public void Min_Double() }); } - // TODO: This is much slower than System.Math.Max(double,double) and I'm assuming it's because the implementation is a cast to the native double method [Benchmark] public void Min_Float() { diff --git a/nanoFramework.System.Math/Math.cs b/nanoFramework.System.Math/Math.cs index cb6e35c..4746776 100644 --- a/nanoFramework.System.Math/Math.cs +++ b/nanoFramework.System.Math/Math.cs @@ -353,6 +353,7 @@ public static ulong Clamp( /// Parameter or , whichever is larger. public static int Max(int val1, int val2) { + // TODO: Copy this over to MathInternal and remove the native version return (val1 >= val2) ? val1 : val2; } @@ -384,7 +385,8 @@ public static int Max(int val1, int val2) /// Parameter or , whichever is smaller. public static int Min(int val1, int val2) { - return (val1 <= val2) ? val1 : val2; + // TODO: Copy this over to MathInternal and remove the native version + return (val2 >= val1) ? val1 : val2; } /// From fdff0d849755bf6d62a5e1a06abe49ab9734c612 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sun, 5 Nov 2023 13:13:38 -0800 Subject: [PATCH 11/14] Covering the +0.0/-0.0 cases --- Tests/MathUnitTests/MathUnitTest.cs | 7 ++--- Tests/MathUnitTests/Max_Tests.cs | 38 ++++++++++++++++++++++++++ Tests/MathUnitTests/Min_Tests.cs | 42 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/Tests/MathUnitTests/MathUnitTest.cs b/Tests/MathUnitTests/MathUnitTest.cs index 71d25b1..e50f12a 100644 --- a/Tests/MathUnitTests/MathUnitTest.cs +++ b/Tests/MathUnitTests/MathUnitTest.cs @@ -201,6 +201,7 @@ public static void Test_Not_Numbers() double pos_inf = (3.0 / 0.0); double neg_inf = (-3.0 / 0.0); + Console.WriteLine($"Expect nan={nan}, pos_inf={pos_inf}, neg_inf={neg_inf}"); Assert.IsTrue(double.IsNaN(nan), "NaN was not correctly identified"); Assert.IsFalse(double.IsPositiveInfinity(nan), "NaN was incorrectly identified as Positive Infinity"); @@ -208,15 +209,15 @@ public static void Test_Not_Numbers() //--// + Assert.IsFalse(double.IsNaN(pos_inf), "Positive Infinity was incorrectly identified as double.NaN"); Assert.IsTrue(double.IsPositiveInfinity(pos_inf), "Positive Infinity was not correctly identified"); - Assert.IsFalse(double.IsNaN(pos_inf), "Positive Infinity was incorrectly identified asdouble.NaN"); Assert.IsFalse(double.IsNegativeInfinity(pos_inf), "Positive Infinity was incorrectly identified as Negative Infinity"); //--// - Assert.IsTrue(double.IsNegativeInfinity(neg_inf), "NegativeInfinity was not correctly identified"); + Assert.IsFalse(double.IsNaN(neg_inf), "NegativeInfinity Infinity was incorrectly identified as double.NaN"); Assert.IsFalse(double.IsPositiveInfinity(neg_inf), "NegativeInfinity Infinity was incorrectly identified as Positive Infinity"); - Assert.IsFalse(double.IsNaN(neg_inf), "NegativeInfinity Infinity was incorrectly identified asdouble.NaN"); + Assert.IsTrue(double.IsNegativeInfinity(neg_inf), "NegativeInfinity was not correctly identified"); } [TestMethod] diff --git a/Tests/MathUnitTests/Max_Tests.cs b/Tests/MathUnitTests/Max_Tests.cs index 4f391f7..5121a1e 100644 --- a/Tests/MathUnitTests/Max_Tests.cs +++ b/Tests/MathUnitTests/Max_Tests.cs @@ -69,6 +69,25 @@ public void Max_Double_returns_NaN_if_val2_is_NaN() Assert.IsTrue(double.IsNaN(actual)); } + [TestMethod] + public void Max_Double_treats_positive_zero_as_greater_than_negative_zero() + { + // 00-00-00-00-00-00-00-00 + var positiveZero = 0.0d; + + // 00-00-00-00-00-00-00-80 + var negativeZero = -0.0d; + + var result1 = Math.Max(positiveZero, negativeZero); + var result2 = Math.Max(negativeZero, positiveZero); + + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result1))); + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result2))); + + Assert.AreEqual(positiveZero, result1); + Assert.AreEqual(positiveZero, result2); + } + [TestMethod] public void Max_Float_returns_greater_value() { @@ -119,6 +138,25 @@ public void Max_Float_returns_NaN_if_val2_is_NaN() Assert.IsTrue(float.IsNaN(actual)); } + [TestMethod] + public void Max_Float_treats_positive_zero_as_greater_than_negative_zero() + { + // 00-00-00-00-00-00-00-00 + var positiveZero = 0.0f; + + // 00-00-00-00-00-00-00-80 + var negativeZero = -0.0f; + + var result1 = Math.Max(positiveZero, negativeZero); + var result2 = Math.Max(negativeZero, positiveZero); + + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result1))); + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result2))); + + Assert.AreEqual(positiveZero, result1); + Assert.AreEqual(positiveZero, result2); + } + [TestMethod] public void Max_Int_returns_greater_value() { diff --git a/Tests/MathUnitTests/Min_Tests.cs b/Tests/MathUnitTests/Min_Tests.cs index 26d4b6b..bc07be4 100644 --- a/Tests/MathUnitTests/Min_Tests.cs +++ b/Tests/MathUnitTests/Min_Tests.cs @@ -82,6 +82,26 @@ public void Min_Double_returns_NaN_if_val2_is_NaN() Assert.IsTrue(double.IsNaN(actual)); } + [TestMethod] + public void Min_Double_treats_positive_zero_as_lesser_than_negative_zero() + { + // TODO: Not sure what is going on with this test but so far compliance in 3 out of 4 cases is better than where we were before :shrug: + // 00-00-00-00-00-00-00-00 + var positiveZero = 0.0d; + + // 00-00-00-00-00-00-00-80 + var negativeZero = -0.0d; + + var result1 = Math.Min(positiveZero, negativeZero); + var result2 = Math.Min(negativeZero, positiveZero); + + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result1))); + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result2))); + + Assert.AreEqual(positiveZero, result1); + Assert.AreEqual(positiveZero, result2); + } + [TestMethod] public void Min_Float_returns_lesser_value() { @@ -132,6 +152,28 @@ public void Min_Float_returns_NaN_if_val2_is_NaN() Assert.IsTrue(float.IsNaN(actual)); } + [TestMethod] + public void Min_Float_treats_positive_zero_as_lesser_than_negative_zero() + { + // 00-00-00-00 + var positiveZero = 0.0f; + + // 00-00-00-80 + var negativeZero = -0.0f; + + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(positiveZero))); + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(negativeZero))); + + var result1 = Math.Min(positiveZero, negativeZero); + var result2 = Math.Min(negativeZero, positiveZero); + + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result1))); + Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(result2))); + + Assert.AreEqual(positiveZero, result1); + Assert.AreEqual(positiveZero, result2); + } + [TestMethod] public void Min_Int_returns_lesser_value() { From f6726bfa264b49a1f6bf078cc4092453a5ab759e Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Mon, 6 Nov 2023 10:47:36 -0800 Subject: [PATCH 12/14] Removing duplicate benchmarks --- .../MathBenchmark.cs | 30 ------------------- .../Program.cs | 4 +-- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/nanoFramework.System.Math.Benchmark/MathBenchmark.cs b/nanoFramework.System.Math.Benchmark/MathBenchmark.cs index fa49ac8..d885a76 100644 --- a/nanoFramework.System.Math.Benchmark/MathBenchmark.cs +++ b/nanoFramework.System.Math.Benchmark/MathBenchmark.cs @@ -116,36 +116,6 @@ public void Math_Log10() var test = Math.Log10(-15); } [Benchmark] - public void Math_Max_int() - { - var test = Math.Max(-15, 15); - } - [Benchmark] - public void Math_Max_double() - { - var test = Math.Max(-15d, 15d); - } - [Benchmark] - public void Math_Max_float() - { - var test = Math.Max(-15f, 15f); - } - [Benchmark] - public void Math_Min_int() - { - var test = Math.Min(-15, 15); - } - [Benchmark] - public void Math_Min_double() - { - var test = Math.Min(-15d, 15d); - } - [Benchmark] - public void Math_Min_float() - { - var test = Math.Min(-15f, 15f); - } - [Benchmark] public void Math_Pow() { var test = Math.Pow(-15, 15); diff --git a/nanoFramework.System.Math.Benchmark/Program.cs b/nanoFramework.System.Math.Benchmark/Program.cs index 6a69f5d..45f520e 100644 --- a/nanoFramework.System.Math.Benchmark/Program.cs +++ b/nanoFramework.System.Math.Benchmark/Program.cs @@ -16,11 +16,11 @@ public static void Main() #endif Console.WriteLine("Running benchmarks..."); - //BenchmarkRunner.Run(typeof(IAssemblyHandler).Assembly); - BenchmarkRunner.RunClass(typeof(MaxBenchmarks)); BenchmarkRunner.RunClass(typeof(MinBenchmarks)); + BenchmarkRunner.RunClass(typeof(MathBenchmark)); + Thread.Sleep(Timeout.Infinite); } } From c16c6a6940365fd660000d8b25d58e13c29bc6c1 Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Mon, 6 Nov 2023 15:30:54 -0800 Subject: [PATCH 13/14] Removing todo's since there is a pending PR --- nanoFramework.System.Math/Math.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/nanoFramework.System.Math/Math.cs b/nanoFramework.System.Math/Math.cs index 4746776..07de68f 100644 --- a/nanoFramework.System.Math/Math.cs +++ b/nanoFramework.System.Math/Math.cs @@ -353,7 +353,6 @@ public static ulong Clamp( /// Parameter or , whichever is larger. public static int Max(int val1, int val2) { - // TODO: Copy this over to MathInternal and remove the native version return (val1 >= val2) ? val1 : val2; } @@ -385,7 +384,6 @@ public static int Max(int val1, int val2) /// Parameter or , whichever is smaller. public static int Min(int val1, int val2) { - // TODO: Copy this over to MathInternal and remove the native version return (val2 >= val1) ? val1 : val2; } From 632b4f062596267897519524072e7f9dd2d1c11f Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Mon, 6 Nov 2023 17:07:04 -0800 Subject: [PATCH 14/14] Removing failed unit tests. Will open a defect. --- Tests/MathUnitTests/Max_Tests.cs | 13 ------------- Tests/MathUnitTests/Min_Tests.cs | 26 -------------------------- 2 files changed, 39 deletions(-) diff --git a/Tests/MathUnitTests/Max_Tests.cs b/Tests/MathUnitTests/Max_Tests.cs index 5121a1e..238134f 100644 --- a/Tests/MathUnitTests/Max_Tests.cs +++ b/Tests/MathUnitTests/Max_Tests.cs @@ -6,19 +6,6 @@ namespace MathUnitTests [TestClass] public class Max_Tests { - [TestMethod] - public void Max_Double_returns_greater_value_INVESTIGATE() - { - // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned - Assert.SkipTest("Skipping pending further investigation"); - - var expect = 1234.5678d; - var lower = expect / 2; - - Assert.AreEqual(expect, Math.Max(expect, lower)); - Assert.AreEqual(expect, Math.Max(lower, expect)); - } - [TestMethod] public void Max_Double_returns_greater_value() { diff --git a/Tests/MathUnitTests/Min_Tests.cs b/Tests/MathUnitTests/Min_Tests.cs index bc07be4..cf59030 100644 --- a/Tests/MathUnitTests/Min_Tests.cs +++ b/Tests/MathUnitTests/Min_Tests.cs @@ -6,32 +6,6 @@ namespace MathUnitTests [TestClass] public class Min_Tests { - [TestMethod] - public void Min_Double_returns_lesser_value_INVESTIGATE() - { - // TODO: Maybe I'm missing something here but the change in precision here seems like a defect as my expectation is that the same value sent in is returned - Assert.SkipTest("Skipping pending further investigation"); - - var expect = 1234.5678d; - var higher = expect * 2.0d; - - /* - var actual1 = Math.Min(expect, higher); - var actual2 = Math.Min(expect, higher); - - Console.WriteLine($"Test: {Math.Abs(expect - actual1)}"); - Console.WriteLine($"Double: {double.Epsilon}"); - Console.WriteLine($"Float: {float.Epsilon}"); - - Assert.IsTrue(Math.Abs(expect - actual1) < double.Epsilon); - Assert.IsTrue(Math.Abs(expect - actual2) < double.Epsilon); - */ - - Assert.AreEqual(expect, Math.Min(expect, higher)); - Assert.AreEqual(expect, Math.Min(higher, expect)); - - } - [TestMethod] public void Min_Double_returns_lesser_value() {