From 3fdcb690455020d9f995ef9718ed8ee4ee839f72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 13:20:12 +0000 Subject: [PATCH 1/3] Initial plan From b24186f2c8f84771aa6ae75a82f858a7d01e5ff6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 13:49:56 +0000 Subject: [PATCH 2/3] Add new math function tests (in-progress) --- Tests/TestAsinhAcoshNew.cs | 133 +++++++++++++++++++++++++++++++++++++ Tests/TestExpNew.cs | 129 +++++++++++++++++++++++++++++++++++ Tests/TestPowNew.cs | 57 ++++++++++++++++ Tests/TestSinCosNew.cs | 93 ++++++++++++++++++++++++++ Tests/TestSinhCoshNew.cs | 93 ++++++++++++++++++++++++++ Tests/TestTanNew.cs | 53 +++++++++++++++ Tests/TestTanhNew.cs | 53 +++++++++++++++ 7 files changed, 611 insertions(+) create mode 100644 Tests/TestAsinhAcoshNew.cs create mode 100644 Tests/TestExpNew.cs create mode 100644 Tests/TestPowNew.cs create mode 100644 Tests/TestSinCosNew.cs create mode 100644 Tests/TestSinhCoshNew.cs create mode 100644 Tests/TestTanNew.cs create mode 100644 Tests/TestTanhNew.cs diff --git a/Tests/TestAsinhAcoshNew.cs b/Tests/TestAsinhAcoshNew.cs new file mode 100644 index 0000000..b2a4723 --- /dev/null +++ b/Tests/TestAsinhAcoshNew.cs @@ -0,0 +1,133 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestAsinhAcoshNew +{ + [Test] + [Parallelizable] + public void FloatTestAsinh() + { + Utils.AssertUlpRate(nameof(FloatTestAsinh), 1000, 0.9, 100, + (-10f, 10f), + x => simd_math.Asinh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Asinh + ); + } + + [Test] + [Parallelizable] + public void FloatTestAsinh_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Asinh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Asinh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestAsinh() + { + Utils.AssertUlpRate(nameof(DoubleTestAsinh), 1000, 0.9, 700, + (-10.0, 10.0), + x => simd_math.Asinh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Asinh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestAsinh_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Asinh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Asinh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestAcosh() + { + Utils.AssertUlpRate(nameof(FloatTestAcosh), 1000, 0.9, 2, + (1f, 10f), + x => simd_math.Acosh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Acosh + ); + } + + [Test] + [Parallelizable] + public void FloatTestAcosh_vec_2_4([Random(1f, 10.0f, 1000)] float v) + { + var a = simd_math.Acosh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Acosh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestAcosh() + { + Utils.AssertUlpRate(nameof(DoubleTestAcosh), 1000, 0.9, 16, + (1.0, 10.0), + x => simd_math.Acosh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Acosh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestAcosh_vec_2_4([Random(1.0, 10.0, 1000)] double v) + { + var a = simd_math.Acosh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Acosh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestAtanh() + { + Utils.AssertUlpRate(nameof(FloatTestAtanh), 1000, 0.9, 4, + (-1f, 1f), + x => simd_math.Atanh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Atanh + ); + } + + [Test] + [Parallelizable] + public void FloatTestAtanh_vec_2_4([Random(-1f, 1.0f, 1000)] float v) + { + var a = simd_math.Atanh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Atanh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestAtanh() + { + Utils.AssertUlpRate(nameof(DoubleTestAtanh), 1000, 0.9, 4, + (-1.0, 1.0), + x => simd_math.Atanh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Atanh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestAtanh_vec_2_4([Random(-1.0, 1.0, 1000)] double v) + { + var a = simd_math.Atanh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Atanh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } +} + +#endif diff --git a/Tests/TestExpNew.cs b/Tests/TestExpNew.cs new file mode 100644 index 0000000..9dce6e6 --- /dev/null +++ b/Tests/TestExpNew.cs @@ -0,0 +1,129 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestExpNew +{ + [Test] + [Parallelizable] + public void FloatTestExp() + { + Utils.AssertUlpRate(nameof(FloatTestExp), 1000, 0.9, 64, + (-60f, 60f), + x => simd_math.Exp(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Exp + ); + } + + [Test] + [Parallelizable] + public void FloatTestExp_vec_2_4([Random(-60f, 60.0f, 1000)] float v) + { + var a = simd_math.Exp(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Exp(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestExpErr([Values(0f, float.NaN, float.NegativeInfinity, float.PositiveInfinity)] float v) + { + var a = simd_math.Exp(new float4(v).UnsafeGetInner()).GetElement(0); + var b = MathF.Exp(v); + Assert.That(b, Is.EqualTo(a)); + } + + [Test] + [Parallelizable] + public void FloatTestExp2() + { + Utils.AssertUlpRate(nameof(FloatTestExp2), 1000, 0.9, 2, + (-60f, 60f), + x => simd_math.Exp2(new float4(x).UnsafeGetInner()).GetElement(0), + x => MathF.Pow(2, x) + ); + } + + [Test] + [Parallelizable] + public void FloatTestExp2_vec_2_4([Random(-60f, 60.0f, 1000)] float v) + { + var a = simd_math.Exp2(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Exp2(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestExp2Err([Values(0f, float.NaN, float.NegativeInfinity, float.PositiveInfinity)] float v) + { + var a = simd_math.Exp2(new float4(v).UnsafeGetInner()).GetElement(0); + var b = MathF.Pow(2, v); + Assert.That(b, Is.EqualTo(a)); + } + + [Test] + [Parallelizable] + public void DoubleTestExp() + { + Utils.AssertUlpRate(nameof(DoubleTestExp), 1000, 0.9, 300, + (-600.0, 600.0), + x => simd_math.Exp(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Exp + ); + } + + [Test] + [Parallelizable] + public void DoubleTestExp_vec_2_4([Random(-600.0, 600.0, 1000)] double v) + { + var a = simd_math.Exp(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Exp(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestExpErr([Values(0.0, double.NaN, double.NegativeInfinity, double.PositiveInfinity)] double v) + { + var a = simd_math.Exp(new double4(v).UnsafeGetInner()).GetElement(0); + var b = Math.Exp(v); + Assert.That(b, Is.EqualTo(a)); + } + + [Test] + [Parallelizable] + public void DoubleTestExp2() + { + Utils.AssertUlpRate(nameof(DoubleTestExp2), 1000, 0.9, 300, + (-600.0, 600.0), + x => simd_math.Exp2(new double4(x).UnsafeGetInner()).GetElement(0), + x => Math.Pow(2, x) + ); + } + + [Test] + [Parallelizable] + public void DoubleTestExp2_vec_2_4([Random(-600.0, 600.0, 1000)] double v) + { + var a = simd_math.Exp2(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Exp2(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestExp2Err([Values(0.0, double.NaN, double.NegativeInfinity, double.PositiveInfinity)] double v) + { + var a = simd_math.Exp2(new double4(v).UnsafeGetInner()).GetElement(0); + var b = Math.Pow(2, v); + Assert.That(b, Is.EqualTo(a)); + } +} + +#endif diff --git a/Tests/TestPowNew.cs b/Tests/TestPowNew.cs new file mode 100644 index 0000000..ca53204 --- /dev/null +++ b/Tests/TestPowNew.cs @@ -0,0 +1,57 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestPowNew +{ + [Test] + [Parallelizable] + public void FloatTestPow() + { + Utils.AssertUlpRate(nameof(FloatTestPow), 1000, 0.9, 16, + (0.001f, 10f), (-10f, 10f), + (x, y) => simd_math.Pow(new float4(x).UnsafeGetInner(), new float4(y).UnsafeGetInner()).GetElement(0), + MathF.Pow + ); + } + + [Test] + [Parallelizable] + public void FloatTestPowOf2() + { + Utils.AssertUlpRate(nameof(FloatTestPowOf2), 1000, 0.9, 2, + (0f, 32f), + x => simd_math.Pow(new float4(2).UnsafeGetInner(), new float4(x).UnsafeGetInner()).GetElement(0), + x => MathF.Pow(2, x) + ); + } + + [Test] + [Parallelizable] + public void DoubleTestPow() + { + Utils.AssertUlpRate(nameof(DoubleTestPow), 1000, 0.9, 16, + (0.001, 10.0), (-10.0, 10.0), + (x, y) => simd_math.Pow(new double4(x).UnsafeGetInner(), new double4(y).UnsafeGetInner()).GetElement(0), + Math.Pow + ); + } + + [Test] + [Parallelizable] + public void DoubleTestPowOf2() + { + Utils.AssertUlpRate(nameof(DoubleTestPowOf2), 1000, 0.9, 700, + (0.0, 32.0), + x => simd_math.Pow(new double4(2).UnsafeGetInner(), new double4(x).UnsafeGetInner()).GetElement(0), + x => Math.Pow(2, x) + ); + } +} + +#endif diff --git a/Tests/TestSinCosNew.cs b/Tests/TestSinCosNew.cs new file mode 100644 index 0000000..371b3a4 --- /dev/null +++ b/Tests/TestSinCosNew.cs @@ -0,0 +1,93 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestSinCosNew +{ + [Test] + [Parallelizable] + public void FloatTestSin() + { + Utils.AssertUlpRate(nameof(FloatTestSin), 1000, 0.9, 500, + (-10f, 10f), + x => simd_math.Sin(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Sin + ); + } + + [Test] + [Parallelizable] + public void FloatTestSin_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Sin(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Sin(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestSin() + { + Utils.AssertUlpRate(nameof(DoubleTestSin), 1000, 0.9, 200, + (-10.0, 10.0), + x => simd_math.Sin(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Sin + ); + } + + [Test] + [Parallelizable] + public void DoubleTestSin_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Sin(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Sin(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestCos() + { + Utils.AssertUlpRate(nameof(FloatTestCos), 1000, 0.9, 700, + (-10f, 10f), + x => simd_math.Cos(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Cos + ); + } + + [Test] + [Parallelizable] + public void FloatTestCos_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Cos(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Cos(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestCos() + { + Utils.AssertUlpRate(nameof(DoubleTestCos), 1000, 0.9, 1500, + (-10.0, 10.0), + x => simd_math.Cos(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Cos + ); + } + + [Test] + [Parallelizable] + public void DoubleTestCos_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Cos(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Cos(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } +} + +#endif diff --git a/Tests/TestSinhCoshNew.cs b/Tests/TestSinhCoshNew.cs new file mode 100644 index 0000000..40ad1e3 --- /dev/null +++ b/Tests/TestSinhCoshNew.cs @@ -0,0 +1,93 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestSinhCoshNew +{ + [Test] + [Parallelizable] + public void FloatTestSinh() + { + Utils.AssertUlpRate(nameof(FloatTestSinh), 1000, 0.9, 64, + (-10f, 10f), + x => simd_math.Sinh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Sinh + ); + } + + [Test] + [Parallelizable] + public void FloatTestSinh_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Sinh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Sinh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestSinh() + { + Utils.AssertUlpRate(nameof(DoubleTestSinh), 1000, 0.9, 256, + (-10.0, 10.0), + x => simd_math.Sinh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Sinh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestSinh_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Sinh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Sinh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void FloatTestCosh() + { + Utils.AssertUlpRate(nameof(FloatTestCosh), 1000, 0.9, 16, + (-10f, 10f), + x => simd_math.Cosh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Cosh + ); + } + + [Test] + [Parallelizable] + public void FloatTestCosh_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Cosh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Cosh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestCosh() + { + Utils.AssertUlpRate(nameof(DoubleTestCosh), 1000, 0.9, 16, + (-10.0, 10.0), + x => simd_math.Cosh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Cosh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestCosh_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Cosh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Cosh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } +} + +#endif diff --git a/Tests/TestTanNew.cs b/Tests/TestTanNew.cs new file mode 100644 index 0000000..067717c --- /dev/null +++ b/Tests/TestTanNew.cs @@ -0,0 +1,53 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestTanNew +{ + [Test] + [Parallelizable] + public void FloatTestTan() + { + Utils.AssertUlpRate(nameof(FloatTestTan), 1000, 0.9, 3000, + (-10f, 10f), + x => simd_math.Tan(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Tan + ); + } + + [Test] + [Parallelizable] + public void FloatTestTan_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Tan(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Tan(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestTan() + { + Utils.AssertUlpRate(nameof(DoubleTestTan), 1000, 0.9, 8000, + (-10.0, 10.0), + x => simd_math.Tan(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Tan + ); + } + + [Test] + [Parallelizable] + public void DoubleTestTan_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Tan(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Tan(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } +} + +#endif diff --git a/Tests/TestTanhNew.cs b/Tests/TestTanhNew.cs new file mode 100644 index 0000000..c4c3b1e --- /dev/null +++ b/Tests/TestTanhNew.cs @@ -0,0 +1,53 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +[Parallelizable] +public class TestTanhNew +{ + [Test] + [Parallelizable] + public void FloatTestTanh() + { + Utils.AssertUlpRate(nameof(FloatTestTanh), 1000, 0.9, 4, + (-10f, 10f), + x => simd_math.Tanh(new float4(x).UnsafeGetInner()).GetElement(0), + MathF.Tanh + ); + } + + [Test] + [Parallelizable] + public void FloatTestTanh_vec_2_4([Random(-10f, 10.0f, 1000)] float v) + { + var a = simd_math.Tanh(new float4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Tanh(new float2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } + + [Test] + [Parallelizable] + public void DoubleTestTanh() + { + Utils.AssertUlpRate(nameof(DoubleTestTanh), 1000, 0.9, 4, + (-10.0, 10.0), + x => simd_math.Tanh(new double4(x).UnsafeGetInner()).GetElement(0), + Math.Tanh + ); + } + + [Test] + [Parallelizable] + public void DoubleTestTanh_vec_2_4([Random(-10.0, 10.0, 1000)] double v) + { + var a = simd_math.Tanh(new double4(v).UnsafeGetInner()).GetElement(0); + var b = simd_math.Tanh(new double2(v).UnsafeGetInner()).GetElement(0); + Assert.That(a, Is.EqualTo(b)); + } +} + +#endif From 78007922fa7eefb3b95d5aa6d550e99de3854c4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 14:13:52 +0000 Subject: [PATCH 3/3] Add Benchmarks project and TestExp2Accuracy tests (Within(2).Ulps) --- Benchmarks/BenchExp2.cs | 124 +++++++++++++++++++++++++++++++++++ Benchmarks/Benchmarks.csproj | 28 ++++++++ Benchmarks/Program.cs | 3 + Coplt.Mathematics.sln | 61 +++++++++++++++++ Tests/TestExp2Accuracy.cs | 87 ++++++++++++++++++++++++ 5 files changed, 303 insertions(+) create mode 100644 Benchmarks/BenchExp2.cs create mode 100644 Benchmarks/Benchmarks.csproj create mode 100644 Benchmarks/Program.cs create mode 100644 Tests/TestExp2Accuracy.cs diff --git a/Benchmarks/BenchExp2.cs b/Benchmarks/BenchExp2.cs new file mode 100644 index 0000000..33b09a2 --- /dev/null +++ b/Benchmarks/BenchExp2.cs @@ -0,0 +1,124 @@ +namespace Benchmarks; + +/// Benchmarks for simd_math.Exp2 — float and double, all SIMD widths. +[DisassemblyDiagnoser] +public class BenchExp2 +{ + // 1 024 random-ish floats in [-60, 60] + private static readonly float[] _f32 = GenerateF32(); + private static readonly double[] _f64 = GenerateF64(); + + private static float[] GenerateF32() + { + const int n = 1024; + var a = new float[n]; + for (var i = 0; i < n; i++) + a[i] = (i - n / 2) * (120f / n); // spans [-60, 60] + return a; + } + + private static double[] GenerateF64() + { + const int n = 1024; + var a = new double[n]; + for (var i = 0; i < n; i++) + a[i] = (i - n / 2) * (1200.0 / n); // spans [-600, 600] + return a; + } + + // ── float ────────────────────────────────────────────────────────────── + + [Benchmark(Description = "Exp2 float×2 (Vector64)")] + public float Exp2_f32x2() + { + var acc = Vector64.Zero; + for (var i = 0; i < _f32.Length - 1; i += 2) + { + var v = Vector64.Create(_f32[i], _f32[i + 1]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + [Benchmark(Description = "Exp2 float×4 (Vector128)", Baseline = true)] + public float Exp2_f32x4() + { + var acc = Vector128.Zero; + for (var i = 0; i < _f32.Length - 3; i += 4) + { + var v = Vector128.Create(_f32[i], _f32[i + 1], _f32[i + 2], _f32[i + 3]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + [Benchmark(Description = "Exp2 float×8 (Vector256)")] + public float Exp2_f32x8() + { + var acc = Vector256.Zero; + for (var i = 0; i < _f32.Length - 7; i += 8) + { + var v = Vector256.Create( + _f32[i], _f32[i + 1], _f32[i + 2], _f32[i + 3], + _f32[i + 4], _f32[i + 5], _f32[i + 6], _f32[i + 7]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + [Benchmark(Description = "Exp2 float×16 (Vector512)")] + public float Exp2_f32x16() + { + var acc = Vector512.Zero; + for (var i = 0; i < _f32.Length - 15; i += 16) + { + var v = Vector512.Create( + _f32[i], _f32[i + 1], _f32[i + 2], _f32[i + 3], + _f32[i + 4], _f32[i + 5], _f32[i + 6], _f32[i + 7], + _f32[i + 8], _f32[i + 9], _f32[i + 10], _f32[i + 11], + _f32[i + 12], _f32[i + 13], _f32[i + 14], _f32[i + 15]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + // ── double ───────────────────────────────────────────────────────────── + + [Benchmark(Description = "Exp2 double×2 (Vector128)")] + public double Exp2_f64x2() + { + var acc = Vector128.Zero; + for (var i = 0; i < _f64.Length - 1; i += 2) + { + var v = Vector128.Create(_f64[i], _f64[i + 1]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + [Benchmark(Description = "Exp2 double×4 (Vector256)")] + public double Exp2_f64x4() + { + var acc = Vector256.Zero; + for (var i = 0; i < _f64.Length - 3; i += 4) + { + var v = Vector256.Create(_f64[i], _f64[i + 1], _f64[i + 2], _f64[i + 3]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } + + [Benchmark(Description = "Exp2 double×8 (Vector512)")] + public double Exp2_f64x8() + { + var acc = Vector512.Zero; + for (var i = 0; i < _f64.Length - 7; i += 8) + { + var v = Vector512.Create( + _f64[i], _f64[i + 1], _f64[i + 2], _f64[i + 3], + _f64[i + 4], _f64[i + 5], _f64[i + 6], _f64[i + 7]); + acc += simd_math.Exp2(v); + } + return acc.GetElement(0); + } +} diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj new file mode 100644 index 0000000..07c58f1 --- /dev/null +++ b/Benchmarks/Benchmarks.csproj @@ -0,0 +1,28 @@ + + + + Exe + net9.0 + enable + enable + 13.0 + true + true + + + + + + + + + + + + + + + + + + diff --git a/Benchmarks/Program.cs b/Benchmarks/Program.cs new file mode 100644 index 0000000..b986d99 --- /dev/null +++ b/Benchmarks/Program.cs @@ -0,0 +1,3 @@ +using BenchmarkDotNet.Running; + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); diff --git a/Coplt.Mathematics.sln b/Coplt.Mathematics.sln index 6f0389b..367a767 100644 --- a/Coplt.Mathematics.sln +++ b/Coplt.Mathematics.sln @@ -10,31 +10,92 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenerator", "SourceGe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coplt.Mathematics.Simt", "Coplt.Mathematics.Simt\Coplt.Mathematics.Simt.csproj", "{18B7AA4D-1D2F-406F-9C77-5FA06A592895}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{98399753-5721-4E8E-BB2C-53A4F9E79A80}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|x64.ActiveCfg = Debug|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|x64.Build.0 = Debug|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|x86.ActiveCfg = Debug|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Debug|x86.Build.0 = Debug|Any CPU {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|Any CPU.Build.0 = Release|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|x64.ActiveCfg = Release|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|x64.Build.0 = Release|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|x86.ActiveCfg = Release|Any CPU + {5745D85D-B9C1-43B8-90A0-D820184CBBBB}.Release|x86.Build.0 = Release|Any CPU {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|x64.Build.0 = Debug|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Debug|x86.Build.0 = Debug|Any CPU {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|Any CPU.Build.0 = Release|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|x64.ActiveCfg = Release|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|x64.Build.0 = Release|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|x86.ActiveCfg = Release|Any CPU + {EAEDC447-AA5D-48D6-B285-425C5E8D52B6}.Release|x86.Build.0 = Release|Any CPU {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|x64.Build.0 = Debug|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Debug|x86.Build.0 = Debug|Any CPU {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|Any CPU.Build.0 = Release|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|x64.ActiveCfg = Release|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|x64.Build.0 = Release|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|x86.ActiveCfg = Release|Any CPU + {A8E41063-557E-4421-88A4-4DE59E49295B}.Release|x86.Build.0 = Release|Any CPU {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|x64.Build.0 = Debug|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Debug|x86.Build.0 = Debug|Any CPU {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|Any CPU.Build.0 = Release|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|x64.ActiveCfg = Release|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|x64.Build.0 = Release|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|x86.ActiveCfg = Release|Any CPU + {D7F6B482-EFD3-46E0-964E-4E2CC53CCE0B}.Release|x86.Build.0 = Release|Any CPU {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|x64.ActiveCfg = Debug|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|x64.Build.0 = Debug|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|x86.ActiveCfg = Debug|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Debug|x86.Build.0 = Debug|Any CPU {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|Any CPU.ActiveCfg = Release|Any CPU {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|Any CPU.Build.0 = Release|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|x64.ActiveCfg = Release|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|x64.Build.0 = Release|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|x86.ActiveCfg = Release|Any CPU + {18B7AA4D-1D2F-406F-9C77-5FA06A592895}.Release|x86.Build.0 = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|x64.ActiveCfg = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|x64.Build.0 = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|x86.ActiveCfg = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Debug|x86.Build.0 = Debug|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|Any CPU.Build.0 = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|x64.ActiveCfg = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|x64.Build.0 = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|x86.ActiveCfg = Release|Any CPU + {98399753-5721-4E8E-BB2C-53A4F9E79A80}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/Tests/TestExp2Accuracy.cs b/Tests/TestExp2Accuracy.cs new file mode 100644 index 0000000..4b3f53c --- /dev/null +++ b/Tests/TestExp2Accuracy.cs @@ -0,0 +1,87 @@ +using System.Runtime.Intrinsics; +using Coplt.Mathematics; + +#if NET8_0_OR_GREATER +using Coplt.Mathematics.Simd; + +namespace Tests; + +/// Accuracy tests for simd_math.Exp2 — float and double, all SIMD widths. +[Parallelizable] +public class TestExp2Accuracy +{ + // ── float Exp2 ───────────────────────────────────────────────────────── + + [Test, Parallelizable] + public void Float_Exp2_Vector128([Random(-60f, 60f, 1000)] float x) + { + var result = simd_math.Exp2(new float4(x).UnsafeGetInner()).GetElement(0); + Assert.That(result, Is.EqualTo(MathF.Pow(2f, x)).Within(2).Ulps); + } + + [Test, Parallelizable] + public void Float_Exp2_Vector64([Random(-60f, 60f, 1000)] float x) + { + var result = simd_math.Exp2(new float2(x).UnsafeGetInner()).GetElement(0); + Assert.That(result, Is.EqualTo(MathF.Pow(2f, x)).Within(2).Ulps); + } + + [Test, Parallelizable] + public void Float_Exp2_ConsistentAcrossWidths([Random(-60f, 60f, 500)] float x) + { + var r64 = simd_math.Exp2(new float2(x).UnsafeGetInner()).GetElement(0); + var r128 = simd_math.Exp2(new float4(x).UnsafeGetInner()).GetElement(0); + Assert.That(r64, Is.EqualTo(r128)); + } + + [Test, Parallelizable] + public void Float_Exp2_SpecialValues( + [Values(0f, 1f, -1f, float.NaN, float.NegativeInfinity, float.PositiveInfinity)] float x) + { + var result = simd_math.Exp2(new float4(x).UnsafeGetInner()).GetElement(0); + var expected = MathF.Pow(2f, x); + // NaN: both must be NaN; Infinity: must match exactly; others: within 2 ULP + if (float.IsNaN(expected)) + Assert.That(result, Is.NaN); + else + Assert.That(result, Is.EqualTo(expected).Within(2).Ulps); + } + + // ── double Exp2 ──────────────────────────────────────────────────────── + + [Test, Parallelizable] + public void Double_Exp2_Vector128([Random(-600.0, 600.0, 1000)] double x) + { + var result = simd_math.Exp2(new double2(x).UnsafeGetInner()).GetElement(0); + Assert.That(result, Is.EqualTo(Math.Pow(2.0, x)).Within(2).Ulps); + } + + [Test, Parallelizable] + public void Double_Exp2_Vector256([Random(-600.0, 600.0, 1000)] double x) + { + var result = simd_math.Exp2(new double4(x).UnsafeGetInner()).GetElement(0); + Assert.That(result, Is.EqualTo(Math.Pow(2.0, x)).Within(2).Ulps); + } + + [Test, Parallelizable] + public void Double_Exp2_ConsistentAcrossWidths([Random(-600.0, 600.0, 500)] double x) + { + var r128 = simd_math.Exp2(new double2(x).UnsafeGetInner()).GetElement(0); + var r256 = simd_math.Exp2(new double4(x).UnsafeGetInner()).GetElement(0); + Assert.That(r128, Is.EqualTo(r256)); + } + + [Test, Parallelizable] + public void Double_Exp2_SpecialValues( + [Values(0.0, 1.0, -1.0, double.NaN, double.NegativeInfinity, double.PositiveInfinity)] double x) + { + var result = simd_math.Exp2(new double4(x).UnsafeGetInner()).GetElement(0); + var expected = Math.Pow(2.0, x); + if (double.IsNaN(expected)) + Assert.That(result, Is.NaN); + else + Assert.That(result, Is.EqualTo(expected).Within(2).Ulps); + } +} + +#endif