diff --git a/src/libraries/Common/tests/System/GenericMathTestMemberData.cs b/src/libraries/Common/tests/System/GenericMathTestMemberData.cs index 49feea063c84d7..58bc6810aebdf5 100644 --- a/src/libraries/Common/tests/System/GenericMathTestMemberData.cs +++ b/src/libraries/Common/tests/System/GenericMathTestMemberData.cs @@ -198,6 +198,58 @@ public static IEnumerable CopySignSingle } } + public static IEnumerable AcosDouble + { + get + { + yield return new object[] { double.NegativeInfinity, double.NaN, 0.0 }; + yield return new object[] { -1.0, 3.1415926535897932, DoubleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { -0.78539816339744831, 2.4741354375614093, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(pi / 4) + yield return new object[] { -0.70710678118654752, 2.3561944901923450, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(1 / sqrt(2)) + yield return new object[] { -0.69314718055994531, 2.3366425216139770, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(ln(2)) + yield return new object[] { -0.63661977236758134, 2.2609034181694367, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(2 / pi) + yield return new object[] { -0.43429448190325183, 2.0200512174769663, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(log10(e)) + yield return new object[] { -0.31830988618379067, 1.8947424337268775, DoubleCrossPlatformMachineEpsilon * 10 }; // value: -(1 / pi) + yield return new object[] { -0.0, 1.5707963267948966, DoubleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { double.NaN, double.NaN, 0.0 }; + yield return new object[] { 0.0, 1.5707963267948966, DoubleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { 0.31830988618379067, 1.2468502198629159, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (1 / pi) + yield return new object[] { 0.43429448190325183, 1.1215414361128270, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (log10(e)) + yield return new object[] { 0.63661977236758134, 0.88068923542035660, DoubleCrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { 0.69314718055994531, 0.80495013197581640, DoubleCrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { 0.70710678118654752, 0.78539816339744828, DoubleCrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)), expected: (pi / 4) + yield return new object[] { 0.78539816339744831, 0.66745721602838380, DoubleCrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { 1.0, 0.0, 0.0 }; + yield return new object[] { double.PositiveInfinity, double.NaN, 0.0 }; + } + } + + public static IEnumerable AcosSingle + { + get + { + yield return new object[] { float.NegativeInfinity, float.NaN, 0.0f }; + yield return new object[] { -1.0f, 3.14159274f, SingleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { -0.785398163f, 2.47413540f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(pi / 4) + yield return new object[] { -0.707106781f, 2.35619450f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(1 / sqrt(2)) + yield return new object[] { -0.693147181f, 2.33664250f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(ln(2)) + yield return new object[] { -0.636619772f, 2.26090336f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(2 / pi) + yield return new object[] { -0.434294482f, 2.02005124f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(log10(e)) + yield return new object[] { -0.318309886f, 1.89474249f, SingleCrossPlatformMachineEpsilon * 10 }; // value: -(1 / pi) + yield return new object[] { -0.0f, 1.57079637f, SingleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { float.NaN, float.NaN, 0.0f }; + yield return new object[] { 0.0f, 1.57079637f, SingleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { 0.318309886f, 1.24685025f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (1 / pi) + yield return new object[] { 0.434294482f, 1.12154138f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (log10(e)) + yield return new object[] { 0.636619772f, 0.880689263f, SingleCrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { 0.693147181f, 0.804950058f, SingleCrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { 0.707106781f, 0.785398185f, SingleCrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)), expected: (pi / 4) + yield return new object[] { 0.785398163f, 0.667457163f, SingleCrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { 1.0f, 0.0f, 0.0f }; + yield return new object[] { float.PositiveInfinity, float.NaN, 0.0f }; + } + } + public static IEnumerable AsinDouble { get diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acos.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acos.cs index cdd36a41dc7f65..6e4dee38b61c6c 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acos.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acos.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -29,11 +30,65 @@ public static void Acos(ReadOnlySpan x, Span destination) private readonly struct AcosOperator : IUnaryOperator where T : ITrigonometricFunctions { - public static bool Vectorizable => false; // TODO: Vectorize + public static bool Vectorizable => +#if NET11_0_OR_GREATER + typeof(T) == typeof(float) || typeof(T) == typeof(double); +#else + false; +#endif + public static T Invoke(T x) => T.Acos(x); - public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); - public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); - public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + + public static Vector128 Invoke(Vector128 x) + { +#if NET11_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Acos(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Acos(x.AsSingle()).As(); + } +#else + throw new NotSupportedException(); +#endif + } + + public static Vector256 Invoke(Vector256 x) + { +#if NET11_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Acos(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Acos(x.AsSingle()).As(); + } +#else + throw new NotSupportedException(); +#endif + } + + public static Vector512 Invoke(Vector512 x) + { +#if NET11_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Acos(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Acos(x.AsSingle()).As(); + } +#else + throw new NotSupportedException(); +#endif + } } } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs index 771813177bb19c..63bd0e0f0c2538 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -458,8 +458,8 @@ public static IEnumerable SpanDestinationFunctionsToTest() T? trigTolerance = IsFmaSupported ? null : Helpers.DetermineTolerance(doubleTolerance: 1e-10, floatTolerance: 1e-4f); yield return Create(TensorPrimitives.Acosh, T.Acosh); - yield return Create(TensorPrimitives.AcosPi, T.AcosPi); - yield return Create(TensorPrimitives.Acos, T.Acos); + yield return Create(TensorPrimitives.AcosPi, T.AcosPi, Helpers.DetermineTolerance(doubleTolerance: 2e-9, floatTolerance: 1e-6f)); + yield return Create(TensorPrimitives.Acos, T.Acos, Helpers.DetermineTolerance(doubleTolerance: 2e-9, floatTolerance: 1e-6f)); yield return Create(TensorPrimitives.Asinh, T.Asinh); yield return Create(TensorPrimitives.AsinPi, T.AsinPi); yield return Create(TensorPrimitives.Asin, T.Asin, trigTolerance); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 1cebc6f55a6b81..6bce123cebeab5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -854,6 +854,47 @@ public static Vector128 Asin(Vector128 vector) } } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Acos(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcosDouble>(vector); + } + else + { + return Create( + Vector64.Acos(vector._lower), + Vector64.Acos(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Acos(Vector128 vector) + { + if (IsHardwareAccelerated) + { + if (Vector256.IsHardwareAccelerated) + { + return VectorMath.AcosSingle, Vector128, Vector256, Vector256>(vector); + } + else + { + return VectorMath.AcosSingle, Vector128, Vector128, Vector128>(vector); + } + } + else + { + return Create( + Vector64.Acos(vector._lower), + Vector64.Acos(vector._upper) + ); + } + } + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Cos(Vector128 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index cd446c7646ac03..b4b9b1b3c3534a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -855,6 +855,47 @@ public static Vector256 Asin(Vector256 vector) } } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Acos(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcosDouble>(vector); + } + else + { + return Create( + Vector128.Acos(vector._lower), + Vector128.Acos(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Acos(Vector256 vector) + { + if (IsHardwareAccelerated) + { + if (Vector512.IsHardwareAccelerated) + { + return VectorMath.AcosSingle, Vector256, Vector512, Vector512>(vector); + } + else + { + return VectorMath.AcosSingle, Vector256, Vector256, Vector256>(vector); + } + } + else + { + return Create( + Vector128.Acos(vector._lower), + Vector128.Acos(vector._upper) + ); + } + } + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Cos(Vector256 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index 6e71109c3ab9c5..ed0995fccc7959 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -758,6 +758,40 @@ public static Vector512 Asin(Vector512 vector) } } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Acos(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcosDouble>(vector); + } + else + { + return Create( + Vector256.Acos(vector._lower), + Vector256.Acos(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Acos(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcosSingle, Vector512, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Acos(vector._lower), + Vector256.Acos(vector._upper) + ); + } + } + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Cos(Vector512 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 8655d9778f0529..93bdaa2b837a40 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -820,6 +820,47 @@ public static Vector64 Asin(Vector64 vector) } } + /// Computes the arc cosine of each element in a vector. + /// The vector whose arc cosine is to be computed. + /// A vector whose elements are the arc cosine of the corresponding elements in . + /// The angles are returned in radians, and the input should be in the range [-1, 1]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Acos(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcosDouble>(vector); + } + else + { + return Acos(vector); + } + } + + /// Computes the arc cosine of each element in a vector. + /// The vector whose arc cosine is to be computed. + /// A vector whose elements are the arc cosine of the corresponding elements in . + /// The angles are returned in radians, and the input should be in the range [-1, 1]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Acos(Vector64 vector) + { + if (IsHardwareAccelerated) + { + if (Vector128.IsHardwareAccelerated) + { + return VectorMath.AcosSingle, Vector64, Vector128, Vector128>(vector); + } + else + { + return VectorMath.AcosSingle, Vector64, Vector64, Vector64>(vector); + } + } + else + { + return Acos(vector); + } + } + /// Computes the cos of each element in a vector. /// The vector that will have its Cos computed. /// A vector whose elements are the cos of the elements in . @@ -3696,6 +3737,20 @@ public static Vector64 ShuffleNative(Vector64 vector, Vector64 Acos(Vector64 vector) + where T : ITrigonometricFunctions + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = T.Acos(vector.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + internal static Vector64 Asin(Vector64 vector) where T : ITrigonometricFunctions { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs index bdbe668efa326b..63bed36497a0ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs @@ -3221,5 +3221,175 @@ private static TVectorDouble AsinSingleCoreDouble(TVectorDouble a return ax + ax * g * poly + (TVectorDouble.Create(PIBY2) & gtHalf); } + + public static TVectorDouble AcosDouble(TVectorDouble x) + where TVectorDouble : unmanaged, ISimdVector + { + // This code is based on `acos` from amd/aocl-libm-ose + // Copyright (C) 2021-2022 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Implementation Notes + // -------------------- + // Based on the value of x, acos(x) is calculated as: + // + // 1. If x > 0.5: acos(x) = 2 * asin(sqrt((1 - x) / 2)) + // 2. If x < -0.5: acos(x) = pi - 2 * asin(sqrt((1 + x) / 2)) + // 3. If |x| <= 0.5: acos(x) = pi/2 - asin(x) + // + // asin(x) is approximated using the polynomial: + // x + C1*x^3 + C2*x^5 + ... + C12*x^25 + + // Polynomial coefficients obtained from Sollya + const double C1 = 0.166666666666647700; // 0x1.55555555552aap-3 + const double C2 = 0.075000000004179696; // 0x1.333333337cbaep-4 + const double C3 = 0.044642856781408560; // 0x1.6db6db3c0984p-5 + const double C4 = 0.030381960650355640; // 0x1.f1c72dd86cbafp-6 + const double C5 = 0.022371727970318958; // 0x1.6e89d3ff33aa4p-6 + const double C6 = 0.017360094637841349; // 0x1.1c6d83ae664b6p-6 + const double C7 = 0.013881842859634605; // 0x1.c6e1568b90518p-7 + const double C8 = 0.012189191110336799; // 0x1.8f6a58977fe49p-7 + const double C9 = 0.006449405266899452; // 0x1.a6ab10b3321bp-8 + const double C10 = 0.019725887785684789; // 0x1.43305ebb2428fp-6 + const double C11 = -0.016511752058748410; // -0x1.0e874ec5e3157p-6 + const double C12 = 0.032096272998247702; // 0x1.06eec35b3b142p-5 + + const double PIBY2 = 1.5707963267948966; // 0x1.921fb54442d18p+0 + const double PIBY4 = 0.78539816339744828; // 0x1.921fb54442d18p-1 + + TVectorDouble xneg = TVectorDouble.IsNegative(x); + TVectorDouble ax = TVectorDouble.Abs(x); + + TVectorDouble gtHalf = TVectorDouble.GreaterThan(ax, TVectorDouble.Create(0.5)); + + // For |x| > 0.5: z = 0.5*(1-|x|), y = -2*sqrt(z) + // For |x| <= 0.5: z = |x|*|x|, y = |x| + TVectorDouble z = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(0.5) * (TVectorDouble.One - ax), ax * ax); + TVectorDouble y = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(-2.0) * TVectorDouble.Sqrt(z), ax); + + // Evaluate polynomial: P(z) = C1 + z*(C2 + z*(C3 + ... + z*C12)) + TVectorDouble poly = TVectorDouble.Create(C12); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C11)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C10)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C9)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C8)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C7)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C6)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C5)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C4)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C3)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C2)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C1)); + + // poly = y + y * z * P(z) + poly = y + y * z * poly; + + // Reconstruct acos using split constants for precision: + // |x| > 0.5: A = 0, B = pi/2 + // |x| <= 0.5: A = pi/4, B = pi/4 + // positive x: result = (A - poly) + A + // negative x: result = (B + poly) + B + TVectorDouble aConst = TVectorDouble.Create(PIBY4) & ~gtHalf; + TVectorDouble bConst = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(PIBY2), TVectorDouble.Create(PIBY4)); + + TVectorDouble posResult = (aConst - poly) + aConst; + TVectorDouble negResult = (bConst + poly) + bConst; + + TVectorDouble result = TVectorDouble.ConditionalSelect(xneg, negResult, posResult); + + // Handle special cases: |x| > 1 returns NaN + TVectorDouble absXGreaterThanOne = TVectorDouble.GreaterThan(ax, TVectorDouble.One); + result = TVectorDouble.ConditionalSelect(absXGreaterThanOne, TVectorDouble.Create(double.NaN), result); + + return result; + } + + public static TVectorSingle AcosSingle(TVectorSingle x) + where TVectorSingle : unmanaged, ISimdVector + where TVectorInt32 : unmanaged, ISimdVector + where TVectorDouble : unmanaged, ISimdVector + where TVectorInt64 : unmanaged, ISimdVector + { + // This code is based on `acosf` from amd/aocl-libm-ose + // Copyright (C) 2021-2023 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + TVectorSingle outOfRange = TVectorSingle.GreaterThan(TVectorSingle.Abs(x), TVectorSingle.One); + TVectorSingle xEqualsOne = TVectorSingle.Equals(x, TVectorSingle.One); + TVectorSingle xEqualsNegOne = TVectorSingle.Equals(x, TVectorSingle.Create(-1.0f)); + + TVectorSingle result; + + if (TVectorSingle.ElementCount == TVectorDouble.ElementCount) + { + TVectorDouble dx = Widen(x); + result = Narrow(AcosSingleCoreDouble(dx)); + } + else + { + TVectorDouble dxLo = WidenLower(x); + TVectorDouble dxHi = WidenUpper(x); + result = Narrow( + AcosSingleCoreDouble(dxLo), + AcosSingleCoreDouble(dxHi)); + } + + result = TVectorSingle.ConditionalSelect(outOfRange, TVectorSingle.Create(float.NaN), result); + result = TVectorSingle.ConditionalSelect(xEqualsOne, TVectorSingle.Zero, result); + result = TVectorSingle.ConditionalSelect(xEqualsNegOne, TVectorSingle.Create(float.Pi), result); + + return result; + } + + private static TVectorDouble AcosSingleCoreDouble(TVectorDouble dx) + where TVectorDouble : unmanaged, ISimdVector + { + // Polynomial coefficients from Sollya (AMD aocl-libm-ose acosf.c) + const double C1 = 0.1666679084300995; // 0x1.5555fcp-3 + const double C2 = 0.074944347143173218; // 0x1.32f8d8p-4 + const double C3 = 0.045550186187028885; // 0x1.7525aap-5 + const double C4 = 0.023858169093728065; // 0x1.86e46ap-6 + const double C5 = 0.042635641992092133; // 0x1.5d456cp-5 + + const double PIBY2 = 1.5707963267948966; // 0x1.921fb54442d18p+0 + const double PIBY4 = 0.78539816339744828; // 0x1.921fb54442d18p-1 + + TVectorDouble xneg = TVectorDouble.IsNegative(dx); + TVectorDouble ax = TVectorDouble.Abs(dx); + + TVectorDouble gtHalf = TVectorDouble.GreaterThan(ax, TVectorDouble.Create(0.5)); + + // For |x| > 0.5: z = 0.5*(1-|x|), y = -2*sqrt(z) + // For |x| <= 0.5: z = |x|*|x|, y = |x| + TVectorDouble z = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(0.5) * (TVectorDouble.One - ax), ax * ax); + TVectorDouble y = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(-2.0) * TVectorDouble.Sqrt(z), ax); + + // Evaluate polynomial: P(z) = C1 + z*(C2 + z*(C3 + z*(C4 + z*C5))) + TVectorDouble poly = TVectorDouble.Create(C5); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C4)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C3)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C2)); + poly = TVectorDouble.MultiplyAddEstimate(poly, z, TVectorDouble.Create(C1)); + + // poly = y + y * z * P(z) + poly = y + y * z * poly; + + // Reconstruct acos using split constants for precision: + // |x| > 0.5: A = 0, B = pi/2 + // |x| <= 0.5: A = pi/4, B = pi/4 + // positive x: result = (A - poly) + A + // negative x: result = (B + poly) + B + TVectorDouble aConst = TVectorDouble.Create(PIBY4) & ~gtHalf; + TVectorDouble bConst = TVectorDouble.ConditionalSelect(gtHalf, TVectorDouble.Create(PIBY2), TVectorDouble.Create(PIBY4)); + + TVectorDouble posResult = (aConst - poly) + aConst; + TVectorDouble negResult = (bConst + poly) + bConst; + + return TVectorDouble.ConditionalSelect(xneg, negResult, posResult); + } } } diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index ada0999ed91e96..1a60f29dd7b321 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -19,6 +19,8 @@ public static partial class Vector128 public static System.Runtime.Intrinsics.Vector128 AndNot(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static bool Any(System.Runtime.Intrinsics.Vector128 vector, T value) { throw null; } public static bool AnyWhereAllBitsSet(System.Runtime.Intrinsics.Vector128 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Acos(System.Runtime.Intrinsics.Vector128 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Acos(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Asin(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Asin(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsByte(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -482,6 +484,8 @@ public static partial class Vector256 public static System.Runtime.Intrinsics.Vector256 AndNot(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static bool Any(System.Runtime.Intrinsics.Vector256 vector, T value) { throw null; } public static bool AnyWhereAllBitsSet(System.Runtime.Intrinsics.Vector256 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Acos(System.Runtime.Intrinsics.Vector256 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Acos(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Asin(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Asin(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsByte(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -934,6 +938,8 @@ public static partial class Vector512 public static System.Runtime.Intrinsics.Vector512 AndNot(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static bool Any(System.Runtime.Intrinsics.Vector512 vector, T value) { throw null; } public static bool AnyWhereAllBitsSet(System.Runtime.Intrinsics.Vector512 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Acos(System.Runtime.Intrinsics.Vector512 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Acos(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Asin(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Asin(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsByte(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1385,6 +1391,8 @@ public static partial class Vector64 public static System.Runtime.Intrinsics.Vector64 AndNot(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static bool Any(System.Runtime.Intrinsics.Vector64 vector, T value) { throw null; } public static bool AnyWhereAllBitsSet(System.Runtime.Intrinsics.Vector64 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Acos(System.Runtime.Intrinsics.Vector64 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Acos(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Asin(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Asin(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsByte(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 6246c1231c32d2..b4fa6a2d28a725 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -5305,6 +5305,22 @@ private static void TestCreateSequence(T start, T step) } } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AcosDoubleTest(double value, double expectedResult, double variance) + { + Vector128 actualResult = Vector128.Acos(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AcosSingleTest(float value, float expectedResult, float variance) + { + Vector128 actualResult = Vector128.Acos(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + [Theory] [MemberData(nameof(GenericMathTestMemberData.AsinDouble), MemberType = typeof(GenericMathTestMemberData))] public void AsinDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index 728a87900f314a..9b17733d2f5cd8 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -6481,6 +6481,22 @@ private static void TestCreateSequence(T start, T step) } } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AcosDoubleTest(double value, double expectedResult, double variance) + { + Vector256 actualResult = Vector256.Acos(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AcosSingleTest(float value, float expectedResult, float variance) + { + Vector256 actualResult = Vector256.Acos(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + [Theory] [MemberData(nameof(GenericMathTestMemberData.AsinDouble), MemberType = typeof(GenericMathTestMemberData))] public void AsinDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index d3c430020b5225..edd63125b77c9a 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -6264,6 +6264,22 @@ private static void TestCreateSequence(T start, T step) } } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AcosDoubleTest(double value, double expectedResult, double variance) + { + Vector512 actualResult = Vector512.Acos(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AcosSingleTest(float value, float expectedResult, float variance) + { + Vector512 actualResult = Vector512.Acos(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + [Theory] [MemberData(nameof(GenericMathTestMemberData.AsinDouble), MemberType = typeof(GenericMathTestMemberData))] public void AsinDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index be91c3325549fb..efbf1535db1b6b 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -4579,6 +4579,22 @@ private static void TestCreateSequence(T start, T step) } } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AcosDoubleTest(double value, double expectedResult, double variance) + { + Vector64 actualResult = Vector64.Acos(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AcosSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AcosSingleTest(float value, float expectedResult, float variance) + { + Vector64 actualResult = Vector64.Acos(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + [Theory] [MemberData(nameof(GenericMathTestMemberData.AsinDouble), MemberType = typeof(GenericMathTestMemberData))] public void AsinDoubleTest(double value, double expectedResult, double variance)