From f19c6a3c77933c632f3c32917d03606baf5100c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:52:33 +0000 Subject: [PATCH 1/5] Initial plan From 00bc621b3bd0a33f46f63de870c854bf61731853 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:18:23 +0000 Subject: [PATCH 2/5] Optimize Half.BitIncrement and BitDecrement to use ushort operations Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../System.Private.CoreLib/src/System/Half.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index fc6dd2bd0c423a..56dcddc26f3687 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -1441,9 +1441,9 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination /// public static Half BitDecrement(Half x) { - uint bits = x._value; + ushort bits = x._value; - if (!IsFinite(x)) + if ((~bits & PositiveInfinityBits) == 0) { // NaN returns NaN // -Infinity returns -Infinity @@ -1454,29 +1454,30 @@ public static Half BitDecrement(Half x) if (bits == PositiveZeroBits) { // +0.0 returns -Epsilon - return -Epsilon; + return new Half(SignMask | EpsilonBits); } // Negative values need to be incremented // Positive values need to be decremented - if (IsNegative(x)) + if ((short)bits < 0) { - bits += 1; + bits++; } else { - bits -= 1; + bits--; } - return new Half((ushort)bits); + + return new Half(bits); } /// public static Half BitIncrement(Half x) { - uint bits = x._value; + ushort bits = x._value; - if (!IsFinite(x)) + if ((~bits & PositiveInfinityBits) == 0) { // NaN returns NaN // -Infinity returns MinValue @@ -1487,21 +1488,22 @@ public static Half BitIncrement(Half x) if (bits == NegativeZeroBits) { // -0.0 returns Epsilon - return Epsilon; + return new Half(EpsilonBits); } // Negative values need to be decremented // Positive values need to be incremented - if (IsNegative(x)) + if ((short)bits < 0) { - bits -= 1; + bits--; } else { - bits += 1; + bits++; } - return new Half((ushort)bits); + + return new Half(bits); } /// From 2ac7925175eeeced6d4bca9f3e32d9f911afbf65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:48:15 +0000 Subject: [PATCH 3/5] Add vectorized Half support to TensorPrimitives.BitIncrement/BitDecrement using ushort operations Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 122 ++++++++++++++++- .../netcore/TensorPrimitives.BitIncrement.cs | 123 +++++++++++++++++- .../System.Private.CoreLib/src/System/Half.cs | 30 ++--- 3 files changed, 257 insertions(+), 18 deletions(-) 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..05b349478b1899 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,118 @@ 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 SignMask = 0x8000; + private const ushort PositiveInfinityBits = 0x7C00; + private const ushort PositiveZeroBits = 0x0000; + private const ushort NegativeEpsilonBits = 0x8001; + private const ushort MaxValueBits = 0x7BFF; + + public static bool Vectorizable => true; + + public static short Invoke(short x) + { + ushort bits = (ushort)x; + + // Check if not finite: NaN or Infinity + if ((~bits & PositiveInfinityBits) == 0) + { + // NaN returns NaN, -Infinity returns -Infinity, +Infinity returns MaxValue + return bits == PositiveInfinityBits ? (short)MaxValueBits : x; + } + + // +0.0 returns -Epsilon + if (bits == PositiveZeroBits) + { + return unchecked((short)NegativeEpsilonBits); + } + + // Negative values need to be incremented, positive values need to be decremented + return (short)(bits < SignMask ? bits - 1 : bits + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + Vector128 bits = x.AsUInt16(); + + // General case: negative -> increment, positive -> decrement + Vector128 isNegative = Vector128.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits + Vector128.One, + bits - Vector128.One); + + // Handle special cases with a single conditional select + Vector128 isPositiveZero = Vector128.Equals(bits, Vector128.Zero); + Vector128 specialValue = Vector128.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN or -Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00, -Infinity: bits == 0xFC00 + Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); + Vector128 isNaNOrNegInf = Vector128.GreaterThanOrEqual(absValue, Vector128.Create(PositiveInfinityBits)) & + Vector128.GreaterThanOrEqual(bits, Vector128.Create(SignMask)); + 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.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits + Vector256.One, + bits - Vector256.One); + + // Handle special cases with a single conditional select + Vector256 isPositiveZero = Vector256.Equals(bits, Vector256.Zero); + Vector256 specialValue = Vector256.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN or -Infinity: preserve original value + Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); + Vector256 isNaNOrNegInf = Vector256.GreaterThanOrEqual(absValue, Vector256.Create(PositiveInfinityBits)) & + Vector256.GreaterThanOrEqual(bits, Vector256.Create(SignMask)); + 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.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits + Vector512.One, + bits - Vector512.One); + + // Handle special cases with a single conditional select + Vector512 isPositiveZero = Vector512.Equals(bits, Vector512.Zero); + Vector512 specialValue = Vector512.Create(NegativeEpsilonBits) & isPositiveZero; + + // NaN or -Infinity: preserve original value + Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); + Vector512 isNaNOrNegInf = Vector512.GreaterThanOrEqual(absValue, Vector512.Create(PositiveInfinityBits)) & + Vector512.GreaterThanOrEqual(bits, Vector512.Create(SignMask)); + 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..d23380564bfce0 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,119 @@ 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 SignMask = 0x8000; + private const ushort PositiveInfinityBits = 0x7C00; + private const ushort NegativeInfinityBits = 0xFC00; + private const ushort NegativeZeroBits = 0x8000; + private const ushort EpsilonBits = 0x0001; + private const ushort MinValueBits = 0xFBFF; + + public static bool Vectorizable => true; + + public static short Invoke(short x) + { + ushort bits = (ushort)x; + + // Check if not finite: NaN or Infinity + if ((~bits & PositiveInfinityBits) == 0) + { + // NaN returns NaN, -Infinity returns MinValue, +Infinity returns +Infinity + return bits == NegativeInfinityBits ? unchecked((short)MinValueBits) : x; + } + + // -0.0 returns Epsilon + if (bits == NegativeZeroBits) + { + return (short)EpsilonBits; + } + + // Negative values need to be decremented, positive values need to be incremented + return (short)(bits < SignMask ? bits + 1 : bits - 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + Vector128 bits = x.AsUInt16(); + + // General case: negative -> decrement, positive -> increment + Vector128 isNegative = Vector128.ShiftRightArithmetic(x, 15).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 or +Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00, +Infinity: bits == 0x7C00 + Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); + Vector128 isNaNOrPosInf = Vector128.GreaterThanOrEqual(absValue, Vector128.Create(PositiveInfinityBits)) & + Vector128.LessThan(bits, Vector128.Create(SignMask)); + 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.ShiftRightArithmetic(x, 15).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 or +Infinity: preserve original value + Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); + Vector256 isNaNOrPosInf = Vector256.GreaterThanOrEqual(absValue, Vector256.Create(PositiveInfinityBits)) & + Vector256.LessThan(bits, Vector256.Create(SignMask)); + 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.ShiftRightArithmetic(x, 15).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 or +Infinity: preserve original value + Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); + Vector512 isNaNOrPosInf = Vector512.GreaterThanOrEqual(absValue, Vector512.Create(PositiveInfinityBits)) & + Vector512.LessThan(bits, Vector512.Create(SignMask)); + specialValue |= bits & isNaNOrPosInf; + + Vector512 specialMask = isNegativeZero | isNaNOrPosInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsInt16(); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index 56dcddc26f3687..fc6dd2bd0c423a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -1441,9 +1441,9 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination /// public static Half BitDecrement(Half x) { - ushort bits = x._value; + uint bits = x._value; - if ((~bits & PositiveInfinityBits) == 0) + if (!IsFinite(x)) { // NaN returns NaN // -Infinity returns -Infinity @@ -1454,30 +1454,29 @@ public static Half BitDecrement(Half x) if (bits == PositiveZeroBits) { // +0.0 returns -Epsilon - return new Half(SignMask | EpsilonBits); + return -Epsilon; } // Negative values need to be incremented // Positive values need to be decremented - if ((short)bits < 0) + if (IsNegative(x)) { - bits++; + bits += 1; } else { - bits--; + bits -= 1; } - - return new Half(bits); + return new Half((ushort)bits); } /// public static Half BitIncrement(Half x) { - ushort bits = x._value; + uint bits = x._value; - if ((~bits & PositiveInfinityBits) == 0) + if (!IsFinite(x)) { // NaN returns NaN // -Infinity returns MinValue @@ -1488,22 +1487,21 @@ public static Half BitIncrement(Half x) if (bits == NegativeZeroBits) { // -0.0 returns Epsilon - return new Half(EpsilonBits); + return Epsilon; } // Negative values need to be decremented // Positive values need to be incremented - if ((short)bits < 0) + if (IsNegative(x)) { - bits--; + bits -= 1; } else { - bits++; + bits += 1; } - - return new Half(bits); + return new Half((ushort)bits); } /// From a2d9b8ef9c95b2ac3005c5e3947ab51f9a16fcf4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:53:10 +0000 Subject: [PATCH 4/5] Fix NaN handling in HalfBitIncrement/DecrementOperator Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 26 ++++++++++++------- .../netcore/TensorPrimitives.BitIncrement.cs | 25 +++++++++++------- 2 files changed, 31 insertions(+), 20 deletions(-) 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 05b349478b1899..636ad37d902b0c 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 @@ -198,6 +198,7 @@ public static Vector512 Invoke(Vector512 x) // Half constants private const ushort SignMask = 0x8000; private const ushort PositiveInfinityBits = 0x7C00; + private const ushort NegativeInfinityBits = 0xFC00; private const ushort PositiveZeroBits = 0x0000; private const ushort NegativeEpsilonBits = 0x8001; private const ushort MaxValueBits = 0x7BFF; @@ -241,11 +242,12 @@ public static Vector128 Invoke(Vector128 x) Vector128 isPositiveZero = Vector128.Equals(bits, Vector128.Zero); Vector128 specialValue = Vector128.Create(NegativeEpsilonBits) & isPositiveZero; - // NaN or -Infinity: preserve original value - // NaN: (bits & 0x7FFF) > 0x7C00, -Infinity: bits == 0xFC00 + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); - Vector128 isNaNOrNegInf = Vector128.GreaterThanOrEqual(absValue, Vector128.Create(PositiveInfinityBits)) & - Vector128.GreaterThanOrEqual(bits, Vector128.Create(SignMask)); + 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; @@ -268,10 +270,12 @@ public static Vector256 Invoke(Vector256 x) Vector256 isPositiveZero = Vector256.Equals(bits, Vector256.Zero); Vector256 specialValue = Vector256.Create(NegativeEpsilonBits) & isPositiveZero; - // NaN or -Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); - Vector256 isNaNOrNegInf = Vector256.GreaterThanOrEqual(absValue, Vector256.Create(PositiveInfinityBits)) & - Vector256.GreaterThanOrEqual(bits, Vector256.Create(SignMask)); + 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; @@ -294,10 +298,12 @@ public static Vector512 Invoke(Vector512 x) Vector512 isPositiveZero = Vector512.Equals(bits, Vector512.Zero); Vector512 specialValue = Vector512.Create(NegativeEpsilonBits) & isPositiveZero; - // NaN or -Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // -Infinity: bits == 0xFC00 Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); - Vector512 isNaNOrNegInf = Vector512.GreaterThanOrEqual(absValue, Vector512.Create(PositiveInfinityBits)) & - Vector512.GreaterThanOrEqual(bits, Vector512.Create(SignMask)); + 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; 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 d23380564bfce0..e33a49bbef1522 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 @@ -242,11 +242,12 @@ public static Vector128 Invoke(Vector128 x) Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(NegativeZeroBits)); Vector128 specialValue = Vector128.Create(EpsilonBits) & isNegativeZero; - // NaN or +Infinity: preserve original value - // NaN: (bits & 0x7FFF) > 0x7C00, +Infinity: bits == 0x7C00 + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 Vector128 absValue = bits & Vector128.Create((ushort)0x7FFF); - Vector128 isNaNOrPosInf = Vector128.GreaterThanOrEqual(absValue, Vector128.Create(PositiveInfinityBits)) & - Vector128.LessThan(bits, Vector128.Create(SignMask)); + 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; @@ -269,10 +270,12 @@ public static Vector256 Invoke(Vector256 x) Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(NegativeZeroBits)); Vector256 specialValue = Vector256.Create(EpsilonBits) & isNegativeZero; - // NaN or +Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 Vector256 absValue = bits & Vector256.Create((ushort)0x7FFF); - Vector256 isNaNOrPosInf = Vector256.GreaterThanOrEqual(absValue, Vector256.Create(PositiveInfinityBits)) & - Vector256.LessThan(bits, Vector256.Create(SignMask)); + 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; @@ -295,10 +298,12 @@ public static Vector512 Invoke(Vector512 x) Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(NegativeZeroBits)); Vector512 specialValue = Vector512.Create(EpsilonBits) & isNegativeZero; - // NaN or +Infinity: preserve original value + // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) + // +Infinity: bits == 0x7C00 Vector512 absValue = bits & Vector512.Create((ushort)0x7FFF); - Vector512 isNaNOrPosInf = Vector512.GreaterThanOrEqual(absValue, Vector512.Create(PositiveInfinityBits)) & - Vector512.LessThan(bits, Vector512.Create(SignMask)); + 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; From 4c2d6f9610c16a35e1133a40e62ad09852c6b517 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:32:05 +0000 Subject: [PATCH 5/5] Address Tanner's feedback: use Half.BitIncrement/Decrement, Vector.IsNegative, and Vector.IsZero Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 36 ++++--------------- .../netcore/TensorPrimitives.BitIncrement.cs | 30 +++------------- 2 files changed, 11 insertions(+), 55 deletions(-) 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 636ad37d902b0c..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 @@ -196,35 +196,13 @@ public static Vector512 Invoke(Vector512 x) private readonly struct HalfBitDecrementOperator : IUnaryOperator { // Half constants - private const ushort SignMask = 0x8000; private const ushort PositiveInfinityBits = 0x7C00; private const ushort NegativeInfinityBits = 0xFC00; - private const ushort PositiveZeroBits = 0x0000; private const ushort NegativeEpsilonBits = 0x8001; - private const ushort MaxValueBits = 0x7BFF; public static bool Vectorizable => true; - public static short Invoke(short x) - { - ushort bits = (ushort)x; - - // Check if not finite: NaN or Infinity - if ((~bits & PositiveInfinityBits) == 0) - { - // NaN returns NaN, -Infinity returns -Infinity, +Infinity returns MaxValue - return bits == PositiveInfinityBits ? (short)MaxValueBits : x; - } - - // +0.0 returns -Epsilon - if (bits == PositiveZeroBits) - { - return unchecked((short)NegativeEpsilonBits); - } - - // Negative values need to be incremented, positive values need to be decremented - return (short)(bits < SignMask ? bits - 1 : bits + 1); - } + public static short Invoke(short x) => BitConverter.HalfToInt16Bits(Half.BitDecrement(BitConverter.Int16BitsToHalf(x))); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x) @@ -232,14 +210,14 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = x.AsUInt16(); // General case: negative -> increment, positive -> decrement - Vector128 isNegative = Vector128.ShiftRightArithmetic(x, 15).AsUInt16(); + 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.Equals(bits, Vector128.Zero); + Vector128 isPositiveZero = Vector128.IsZero(bits); Vector128 specialValue = Vector128.Create(NegativeEpsilonBits) & isPositiveZero; // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) @@ -260,14 +238,14 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = x.AsUInt16(); // General case: negative -> increment, positive -> decrement - Vector256 isNegative = Vector256.ShiftRightArithmetic(x, 15).AsUInt16(); + 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.Equals(bits, Vector256.Zero); + Vector256 isPositiveZero = Vector256.IsZero(bits); Vector256 specialValue = Vector256.Create(NegativeEpsilonBits) & isPositiveZero; // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) @@ -288,14 +266,14 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = x.AsUInt16(); // General case: negative -> increment, positive -> decrement - Vector512 isNegative = Vector512.ShiftRightArithmetic(x, 15).AsUInt16(); + 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.Equals(bits, Vector512.Zero); + Vector512 isPositiveZero = Vector512.IsZero(bits); Vector512 specialValue = Vector512.Create(NegativeEpsilonBits) & isPositiveZero; // NaN: (bits & 0x7FFF) > 0x7C00 (both positive and negative NaN) 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 e33a49bbef1522..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 @@ -196,35 +196,13 @@ public static Vector512 Invoke(Vector512 x) private readonly struct HalfBitIncrementOperator : IUnaryOperator { // Half constants - private const ushort SignMask = 0x8000; private const ushort PositiveInfinityBits = 0x7C00; - private const ushort NegativeInfinityBits = 0xFC00; private const ushort NegativeZeroBits = 0x8000; private const ushort EpsilonBits = 0x0001; - private const ushort MinValueBits = 0xFBFF; public static bool Vectorizable => true; - public static short Invoke(short x) - { - ushort bits = (ushort)x; - - // Check if not finite: NaN or Infinity - if ((~bits & PositiveInfinityBits) == 0) - { - // NaN returns NaN, -Infinity returns MinValue, +Infinity returns +Infinity - return bits == NegativeInfinityBits ? unchecked((short)MinValueBits) : x; - } - - // -0.0 returns Epsilon - if (bits == NegativeZeroBits) - { - return (short)EpsilonBits; - } - - // Negative values need to be decremented, positive values need to be incremented - return (short)(bits < SignMask ? bits + 1 : bits - 1); - } + public static short Invoke(short x) => BitConverter.HalfToInt16Bits(Half.BitIncrement(BitConverter.Int16BitsToHalf(x))); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x) @@ -232,7 +210,7 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = x.AsUInt16(); // General case: negative -> decrement, positive -> increment - Vector128 isNegative = Vector128.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector128 isNegative = Vector128.IsNegative(x).AsUInt16(); Vector128 result = Vector128.ConditionalSelect( isNegative, bits - Vector128.One, @@ -260,7 +238,7 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = x.AsUInt16(); // General case: negative -> decrement, positive -> increment - Vector256 isNegative = Vector256.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector256 isNegative = Vector256.IsNegative(x).AsUInt16(); Vector256 result = Vector256.ConditionalSelect( isNegative, bits - Vector256.One, @@ -288,7 +266,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = x.AsUInt16(); // General case: negative -> decrement, positive -> increment - Vector512 isNegative = Vector512.ShiftRightArithmetic(x, 15).AsUInt16(); + Vector512 isNegative = Vector512.IsNegative(x).AsUInt16(); Vector512 result = Vector512.ConditionalSelect( isNegative, bits - Vector512.One,