diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitDecrement.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitDecrement.cs index 2504c0375592be..6518b702e1760b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitDecrement.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitDecrement.cs @@ -20,8 +20,15 @@ public static partial class TensorPrimitives /// /// public static void BitDecrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 => + where T : IFloatingPointIeee754 + { + if (typeof(T) == typeof(Half) && TryUnaryBitwiseInvokeHalfAsInt16(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitDecrement(x) private readonly struct BitDecrementOperator : IUnaryOperator @@ -184,5 +191,102 @@ public static Vector512 Invoke(Vector512 x) throw new NotSupportedException(); } } + + /// Half.BitDecrement(x) operator for direct ushort manipulation. + private readonly struct HalfBitDecrementOperator : IUnaryOperator + { + // Half constants + private const ushort PositiveInfinityBits = 0x7C00; + private const ushort NegativeInfinityBits = 0xFC00; + private const ushort NegativeEpsilonBits = 0x8001; + + public static bool Vectorizable => true; + + public static short Invoke(short x) => BitConverter.HalfToInt16Bits(Half.BitDecrement(BitConverter.Int16BitsToHalf(x))); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + Vector128 bits = x.AsUInt16(); + + // General case: negative -> increment, positive -> decrement + Vector128 isNegative = Vector128.IsNegative(x).AsUInt16(); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits + Vector128.One, + bits - Vector128.One); + + // Handle special cases with a single conditional select + Vector128 isPositiveZero = Vector128.IsZero(bits); + Vector128 specialValue = Vector128.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 + Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); + Vector128 isNaN = Vector128.GreaterThan(absValue, Vector128.Create(PositiveInfinityBits)); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(NegativeInfinityBits)); + Vector128 isNaNOrNegInf = isNaN | isNegInf; + specialValue |= bits & isNaNOrNegInf; + + Vector128 specialMask = isPositiveZero | isNaNOrNegInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x) + { + Vector256 bits = x.AsUInt16(); + + // General case: negative -> increment, positive -> decrement + Vector256 isNegative = Vector256.IsNegative(x).AsUInt16(); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits + Vector256.One, + bits - Vector256.One); + + // Handle special cases with a single conditional select + Vector256 isPositiveZero = Vector256.IsZero(bits); + Vector256 specialValue = Vector256.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 + Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); + Vector256 isNaN = Vector256.GreaterThan(absValue, Vector256.Create(PositiveInfinityBits)); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(NegativeInfinityBits)); + Vector256 isNaNOrNegInf = isNaN | isNegInf; + specialValue |= bits & isNaNOrNegInf; + + Vector256 specialMask = isPositiveZero | isNaNOrNegInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x) + { + Vector512 bits = x.AsUInt16(); + + // General case: negative -> increment, positive -> decrement + Vector512 isNegative = Vector512.IsNegative(x).AsUInt16(); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits + Vector512.One, + bits - Vector512.One); + + // Handle special cases with a single conditional select + Vector512 isPositiveZero = Vector512.IsZero(bits); + Vector512 specialValue = Vector512.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 + Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); + Vector512 isNaN = Vector512.GreaterThan(absValue, Vector512.Create(PositiveInfinityBits)); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(NegativeInfinityBits)); + Vector512 isNaNOrNegInf = isNaN | isNegInf; + specialValue |= bits & isNaNOrNegInf; + + Vector512 specialMask = isPositiveZero | isNaNOrNegInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitIncrement.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitIncrement.cs index 361af7af06576b..b7b1e930c68c9b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitIncrement.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.BitIncrement.cs @@ -20,8 +20,15 @@ public static partial class TensorPrimitives /// /// public static void BitIncrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 => + where T : IFloatingPointIeee754 + { + if (typeof(T) == typeof(Half) && TryUnaryBitwiseInvokeHalfAsInt16(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitIncrement(x) private readonly struct BitIncrementOperator : IUnaryOperator @@ -184,5 +191,102 @@ public static Vector512 Invoke(Vector512 x) throw new NotSupportedException(); } } + + /// Half.BitIncrement(x) operator for direct ushort manipulation. + private readonly struct HalfBitIncrementOperator : IUnaryOperator + { + // Half constants + private const ushort PositiveInfinityBits = 0x7C00; + private const ushort NegativeZeroBits = 0x8000; + private const ushort EpsilonBits = 0x0001; + + public static bool Vectorizable => true; + + public static short Invoke(short x) => BitConverter.HalfToInt16Bits(Half.BitIncrement(BitConverter.Int16BitsToHalf(x))); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + Vector128 bits = x.AsUInt16(); + + // General case: negative -> decrement, positive -> increment + Vector128 isNegative = Vector128.IsNegative(x).AsUInt16(); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits - Vector128.One, + bits + Vector128.One); + + // Handle special cases with a single conditional select + Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(NegativeZeroBits)); + Vector128 specialValue = Vector128.Create(EpsilonBits) & isNegativeZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 + Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); + Vector128 isNaN = Vector128.GreaterThan(absValue, Vector128.Create(PositiveInfinityBits)); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(PositiveInfinityBits)); + Vector128 isNaNOrPosInf = isNaN | isPosInf; + specialValue |= bits & isNaNOrPosInf; + + Vector128 specialMask = isNegativeZero | isNaNOrPosInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x) + { + Vector256 bits = x.AsUInt16(); + + // General case: negative -> decrement, positive -> increment + Vector256 isNegative = Vector256.IsNegative(x).AsUInt16(); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits - Vector256.One, + bits + Vector256.One); + + // Handle special cases with a single conditional select + Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(NegativeZeroBits)); + Vector256 specialValue = Vector256.Create(EpsilonBits) & isNegativeZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 + Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); + Vector256 isNaN = Vector256.GreaterThan(absValue, Vector256.Create(PositiveInfinityBits)); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(PositiveInfinityBits)); + Vector256 isNaNOrPosInf = isNaN | isPosInf; + specialValue |= bits & isNaNOrPosInf; + + Vector256 specialMask = isNegativeZero | isNaNOrPosInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x) + { + Vector512 bits = x.AsUInt16(); + + // General case: negative -> decrement, positive -> increment + Vector512 isNegative = Vector512.IsNegative(x).AsUInt16(); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits - Vector512.One, + bits + Vector512.One); + + // Handle special cases with a single conditional select + Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(NegativeZeroBits)); + Vector512 specialValue = Vector512.Create(EpsilonBits) & isNegativeZero; + + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 + Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); + Vector512 isNaN = Vector512.GreaterThan(absValue, Vector512.Create(PositiveInfinityBits)); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(PositiveInfinityBits)); + Vector512 isNaNOrPosInf = isNaN | isPosInf; + specialValue |= bits & isNaNOrPosInf; + + Vector512 specialMask = isNegativeZero | isNaNOrPosInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + } } }