From 80f174951804d0ab87bd4dc769dba11c8fe93075 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 1 Apr 2026 11:02:44 -0400 Subject: [PATCH 1/2] Vectorize Acosh for Vector64/128/256/512 and TensorPrimitives Add vectorized Acosh implementations for float and double across all SIMD vector types. - AcoshDouble: uses log identity (log(x + sqrt(x^2 - 1))) for vectorization - AcoshSingle: widens to double and calls AcoshDouble - Hook up TensorPrimitives.Acosh to use vectorized implementations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.Acosh.cs | 65 +++++++++++++-- .../tests/TensorPrimitives.Generic.cs | 2 +- .../System/Runtime/Intrinsics/Vector128.cs | 47 +++++++++++ .../System/Runtime/Intrinsics/Vector256.cs | 47 +++++++++++ .../System/Runtime/Intrinsics/Vector512.cs | 40 ++++++++++ .../src/System/Runtime/Intrinsics/Vector64.cs | 55 +++++++++++++ .../System/Runtime/Intrinsics/VectorMath.cs | 79 +++++++++++++++++++ .../ref/System.Runtime.Intrinsics.cs | 8 ++ 8 files changed, 337 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acosh.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acosh.cs index 7313cfe15f2d5d..bbc1f34ed93cdd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acosh.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Acosh.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 @@ -26,14 +27,68 @@ public static void Acosh(ReadOnlySpan x, Span destination) InvokeSpanIntoSpan>(x, destination); /// T.Acosh(x) - private readonly struct AcoshOperator : IUnaryOperator + internal readonly struct AcoshOperator : IUnaryOperator where T : IHyperbolicFunctions { - 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.Acosh(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.Acosh(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Acosh(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.Acosh(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Acosh(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.Acosh(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Acosh(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..16fa3052e9b7b5 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -457,7 +457,7 @@ public static IEnumerable SpanDestinationFunctionsToTest() // The current trigonometric algorithm depends on hardware FMA support for best precision. T? trigTolerance = IsFmaSupported ? null : Helpers.DetermineTolerance(doubleTolerance: 1e-10, floatTolerance: 1e-4f); - yield return Create(TensorPrimitives.Acosh, T.Acosh); + yield return Create(TensorPrimitives.Acosh, T.Acosh, Helpers.DetermineTolerance(doubleTolerance: 1e-14, floatTolerance: 1e-6f)); yield return Create(TensorPrimitives.AcosPi, T.AcosPi); yield return Create(TensorPrimitives.Acos, T.Acos); yield return Create(TensorPrimitives.Asinh, T.Asinh); 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 dc20ffbd556bf5..fc14df4cbc01fc 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,53 @@ public static Vector128 Asin(Vector128 vector) } } + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Acosh(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcoshDouble, Vector128, Vector128>(vector); + } + else + { + return Create( + Vector64.Acosh(vector._lower), + Vector64.Acosh(vector._upper) + ); + } + } + + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Acosh(Vector128 vector) + { + if (IsHardwareAccelerated) + { + if (Vector256.IsHardwareAccelerated) + { + return VectorMath.AcoshSingle, Vector128, Vector128, Vector256, Vector256, Vector256>(vector); + } + else + { + return VectorMath.AcoshSingle, Vector128, Vector128, Vector128, Vector128, Vector128>(vector); + } + } + else + { + return Create( + Vector64.Acosh(vector._lower), + Vector64.Acosh(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 c51c1f5329ef74..69b699375d73a2 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,53 @@ public static Vector256 Asin(Vector256 vector) } } + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Acosh(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcoshDouble, Vector256, Vector256>(vector); + } + else + { + return Create( + Vector128.Acosh(vector._lower), + Vector128.Acosh(vector._upper) + ); + } + } + + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Acosh(Vector256 vector) + { + if (IsHardwareAccelerated) + { + if (Vector512.IsHardwareAccelerated) + { + return VectorMath.AcoshSingle, Vector256, Vector256, Vector512, Vector512, Vector512>(vector); + } + else + { + return VectorMath.AcoshSingle, Vector256, Vector256, Vector256, Vector256, Vector256>(vector); + } + } + else + { + return Create( + Vector128.Acosh(vector._lower), + Vector128.Acosh(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 5bc4b5e0964a52..c2267540a49aa5 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,46 @@ public static Vector512 Asin(Vector512 vector) } } + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Acosh(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcoshDouble, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Acosh(vector._lower), + Vector256.Acosh(vector._upper) + ); + } + } + + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Acosh(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcoshSingle, Vector512, Vector512, Vector512, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Acosh(vector._lower), + Vector256.Acosh(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 7077fe391347e6..6382ec6d536f30 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 @@ -819,6 +819,47 @@ public static Vector64 Asin(Vector64 vector) } } + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Acosh(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.AcoshDouble, Vector64, Vector64>(vector); + } + else + { + return Acosh(vector); + } + } + + /// Computes the inverse hyperbolic cosine of each element in a vector. + /// The vector whose inverse hyperbolic cosine is to be computed. + /// A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in . + /// The input should be greater than or equal to 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Acosh(Vector64 vector) + { + if (IsHardwareAccelerated) + { + if (Vector128.IsHardwareAccelerated) + { + return VectorMath.AcoshSingle, Vector64, Vector64, Vector128, Vector128, Vector128>(vector); + } + else + { + return VectorMath.AcoshSingle, Vector64, Vector64, Vector64, Vector64, Vector64>(vector); + } + } + else + { + return Acosh(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 . @@ -3713,6 +3754,20 @@ internal static Vector64 Asin(Vector64 vector) return result; } + internal static Vector64 Acosh(Vector64 vector) + where T : IHyperbolicFunctions + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = T.Acosh(vector.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + internal static Vector64 Sin(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..c179e6c0710e14 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,84 @@ private static TVectorDouble AsinSingleCoreDouble(TVectorDouble a return ax + ax * g * poly + (TVectorDouble.Create(PIBY2) & gtHalf); } + + public static TVectorDouble AcoshDouble(TVectorDouble x) + where TVectorDouble : unmanaged, ISimdVector + where TVectorInt64 : unmanaged, ISimdVector + where TVectorUInt64 : unmanaged, ISimdVector + { + // The AMD AOCL-LibM scalar acosh implementation (acosh.c) uses range-based + // polynomial lookup tables which cannot be trivially vectorized due to the cost + // of gather instructions. Instead, this uses the mathematical identity: + // acosh(x) = log(x + sqrt(x^2 - 1)) + // with special handling for x near 1 and large x for improved accuracy. + + const double LN2 = 0.693147180559945309417; + const double NEAR_ONE_THRESHOLD = 1.0 + 2.98023223876953125e-08; // 1 + 2^-25 + const double LARGE_THRESHOLD = 268435456.0; // 2^28 + + // Return NaN for x < 1 + TVectorDouble nanMask = TVectorDouble.LessThan(x, TVectorDouble.One); + + // For x close to 1 (1 < x <= 1 + 2^-25), use sqrt(2 * (x - 1)) + TVectorDouble nearOneMask = TVectorDouble.LessThanOrEqual(x, TVectorDouble.Create(NEAR_ONE_THRESHOLD)); + + // For large values (x > 2^28), use log(2) + log(x) + TVectorDouble largeMask = TVectorDouble.GreaterThan(x, TVectorDouble.Create(LARGE_THRESHOLD)); + + // Normal case: log(x + sqrt(x^2 - 1)) + TVectorDouble x2 = x * x; + TVectorDouble sqrtArg = x2 - TVectorDouble.One; + TVectorDouble normal = LogDouble(x + TVectorDouble.Sqrt(sqrtArg)); + + // Large value case: log(2) + log(x) + TVectorDouble large = TVectorDouble.Create(LN2) + LogDouble(x); + + // Near one case: sqrt(2 * (x - 1)) + TVectorDouble nearOne = TVectorDouble.Sqrt(TVectorDouble.Create(2.0) * (x - TVectorDouble.One)); + + // Select appropriate result based on magnitude + TVectorDouble result = TVectorDouble.ConditionalSelect(largeMask, large, normal); + result = TVectorDouble.ConditionalSelect(nearOneMask, nearOne, result); + result = TVectorDouble.ConditionalSelect(nanMask, TVectorDouble.Create(double.NaN), result); + + return result; + } + + public static TVectorSingle AcoshSingle(TVectorSingle x) + where TVectorSingle : unmanaged, ISimdVector + where TVectorInt32 : unmanaged, ISimdVector + where TVectorUInt32 : unmanaged, ISimdVector + where TVectorDouble : unmanaged, ISimdVector + where TVectorInt64 : unmanaged, ISimdVector + where TVectorUInt64 : unmanaged, ISimdVector + { + // This code is based on `acoshf` from amd/aocl-libm-ose + // Copyright (C) 2008-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 + + // AMD acoshf.c uses mathematical identities (no polynomial approximation): + // For x > 1/sqrt(eps): acosh(x) = log(2) + log(x) + // For 2 < x <= 1/sqrt(eps): acosh(x) = log(x + sqrt(x^2 - 1)) + // For sqrt(eps) <= x <= 2: t=x-1, acosh(x) = log1p(t + sqrt(2t + t^2)) + // Widens to double for improved accuracy, matching AMD acoshf.c behavior. + + if (TVectorSingle.ElementCount == TVectorDouble.ElementCount) + { + TVectorDouble dx = Widen(x); + return Narrow(AcoshDouble(dx)); + } + else + { + TVectorDouble dxLo = WidenLower(x); + TVectorDouble dxHi = WidenUpper(x); + return Narrow( + AcoshDouble(dxLo), + AcoshDouble(dxHi) + ); + } + } } } 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..ef42ac72dcd810 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -21,6 +21,8 @@ public static partial class Vector128 public static bool AnyWhereAllBitsSet(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 Acosh(System.Runtime.Intrinsics.Vector128 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Acosh(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsByte(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsDouble(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsInt16(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -484,6 +486,8 @@ public static partial class Vector256 public static bool AnyWhereAllBitsSet(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 Acosh(System.Runtime.Intrinsics.Vector256 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Acosh(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsByte(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsDouble(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsInt16(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -936,6 +940,8 @@ public static partial class Vector512 public static bool AnyWhereAllBitsSet(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 Acosh(System.Runtime.Intrinsics.Vector512 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Acosh(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsByte(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsDouble(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsInt16(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1387,6 +1393,8 @@ public static partial class Vector64 public static bool AnyWhereAllBitsSet(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 Acosh(System.Runtime.Intrinsics.Vector64 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Acosh(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsByte(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsDouble(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsInt16(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } From 3544d6622a612e056dc01513f77f9028fc4150f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:07:47 +0000 Subject: [PATCH 2/2] Address PR feedback: fix alphabetical ordering, improve near-one accuracy, fix comment Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/43d57cf9-0cfe-4d16-9977-8c3294328c02 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../System/Runtime/Intrinsics/VectorMath.cs | 31 +++++++------------ .../ref/System.Runtime.Intrinsics.cs | 16 +++++----- 2 files changed, 20 insertions(+), 27 deletions(-) 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 c179e6c0710e14..0598aeca7260d9 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 @@ -3230,36 +3230,30 @@ public static TVectorDouble AcoshDouble 2^28), use log(2) + log(x) TVectorDouble largeMask = TVectorDouble.GreaterThan(x, TVectorDouble.Create(LARGE_THRESHOLD)); - // Normal case: log(x + sqrt(x^2 - 1)) - TVectorDouble x2 = x * x; - TVectorDouble sqrtArg = x2 - TVectorDouble.One; - TVectorDouble normal = LogDouble(x + TVectorDouble.Sqrt(sqrtArg)); + // Normal case: log(x + sqrt((x - 1) * (x + 1))) + // Using (x-1)*(x+1) avoids catastrophic cancellation when x is near 1 + TVectorDouble xm1 = x - TVectorDouble.One; + TVectorDouble xp1 = x + TVectorDouble.One; + TVectorDouble normal = LogDouble(x + TVectorDouble.Sqrt(xm1 * xp1)); // Large value case: log(2) + log(x) TVectorDouble large = TVectorDouble.Create(LN2) + LogDouble(x); - // Near one case: sqrt(2 * (x - 1)) - TVectorDouble nearOne = TVectorDouble.Sqrt(TVectorDouble.Create(2.0) * (x - TVectorDouble.One)); - // Select appropriate result based on magnitude TVectorDouble result = TVectorDouble.ConditionalSelect(largeMask, large, normal); - result = TVectorDouble.ConditionalSelect(nearOneMask, nearOne, result); result = TVectorDouble.ConditionalSelect(nanMask, TVectorDouble.Create(double.NaN), result); return result; @@ -3279,11 +3273,10 @@ public static TVectorSingle AcoshSingle 1/sqrt(eps): acosh(x) = log(2) + log(x) - // For 2 < x <= 1/sqrt(eps): acosh(x) = log(x + sqrt(x^2 - 1)) - // For sqrt(eps) <= x <= 2: t=x-1, acosh(x) = log1p(t + sqrt(2t + t^2)) - // Widens to double for improved accuracy, matching AMD acoshf.c behavior. + // This implementation computes single-precision acosh by widening the + // input to double precision, calling AcoshDouble, and then narrowing + // the result back to single precision. AcoshDouble uses mathematical + // identities (no polynomial approximation) for improved accuracy. if (TVectorSingle.ElementCount == TVectorDouble.ElementCount) { 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 ef42ac72dcd810..6c891ddd5f22cc 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -19,10 +19,10 @@ 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 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 Acosh(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Acosh(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; } public static System.Runtime.Intrinsics.Vector128 AsDouble(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsInt16(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -484,10 +484,10 @@ 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 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 Acosh(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Acosh(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; } public static System.Runtime.Intrinsics.Vector256 AsDouble(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsInt16(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -938,10 +938,10 @@ 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 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 Acosh(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Acosh(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; } public static System.Runtime.Intrinsics.Vector512 AsDouble(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsInt16(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1391,10 +1391,10 @@ 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 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 Acosh(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Acosh(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; } public static System.Runtime.Intrinsics.Vector64 AsDouble(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsInt16(this System.Runtime.Intrinsics.Vector64 vector) { throw null; }