From e8ae0e1949a750972631018817fcd266c69d0d54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 02:16:08 +0000 Subject: [PATCH 01/14] Initial plan From 93c545f0b633955f0da510f6df3e816e48b4db62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 02:23:00 +0000 Subject: [PATCH 02/14] Implement vectorization for BitIncrement and BitDecrement operations Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 238 +++++++++++++++++- .../netcore/TensorPrimitives.BitIncrement.cs | 238 +++++++++++++++++- 2 files changed, 468 insertions(+), 8 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 b97336b0a2e3a5..987ad3cc8a5cb1 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 @@ -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.Runtime.CompilerServices; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -26,11 +27,240 @@ public static void BitDecrement(ReadOnlySpan x, Span destination) private readonly struct BitDecrementOperator : IUnaryOperator where T : IFloatingPointIeee754 { - public static bool Vectorizable => false; // TODO: Vectorize + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + public static T Invoke(T x) => T.BitDecrement(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(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + Vector128 xFloat = x.AsSingle(); + Vector128 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector128 isNaNMask = Vector128.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector128 isNaN = Vector128.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(float.PositiveInfinityBits)); + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(float.PositiveZeroBits)); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(float.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector128 isNegative = Vector128.LessThan(xFloat, Vector128.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector128 incremented = bits + Vector128.One; + Vector128 decremented = bits - Vector128.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector128 result = Vector128.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector128 preserveOriginal = isNaN | isNegInf; + result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector128 xDouble = x.AsDouble(); + Vector128 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector128 isNaNMask = Vector128.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector128 isNaN = Vector128.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(double.PositiveInfinityBits)); + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(double.PositiveZeroBits)); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(double.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector128 isNegative = Vector128.LessThan(xDouble, Vector128.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector128 incremented = bits + Vector128.One; + Vector128 decremented = bits - Vector128.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector128 result = Vector128.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector128 preserveOriginal = isNaN | isNegInf; + result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + Vector256 xFloat = x.AsSingle(); + Vector256 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector256 isNaNMask = Vector256.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector256 isNaN = Vector256.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(float.PositiveInfinityBits)); + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(float.PositiveZeroBits)); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(float.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector256 isNegative = Vector256.LessThan(xFloat, Vector256.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector256 incremented = bits + Vector256.One; + Vector256 decremented = bits - Vector256.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector256 result = Vector256.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector256 preserveOriginal = isNaN | isNegInf; + result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector256 xDouble = x.AsDouble(); + Vector256 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector256 isNaNMask = Vector256.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector256 isNaN = Vector256.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(double.PositiveInfinityBits)); + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(double.PositiveZeroBits)); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(double.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector256 isNegative = Vector256.LessThan(xDouble, Vector256.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector256 incremented = bits + Vector256.One; + Vector256 decremented = bits - Vector256.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector256 result = Vector256.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector256 preserveOriginal = isNaN | isNegInf; + result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + Vector512 xFloat = x.AsSingle(); + Vector512 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector512 isNaNMask = Vector512.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector512 isNaN = Vector512.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(float.PositiveInfinityBits)); + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(float.PositiveZeroBits)); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(float.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector512 isNegative = Vector512.LessThan(xFloat, Vector512.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector512 incremented = bits + Vector512.One; + Vector512 decremented = bits - Vector512.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector512 result = Vector512.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector512 preserveOriginal = isNaN | isNegInf; + result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector512 xDouble = x.AsDouble(); + Vector512 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector512 isNaNMask = Vector512.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector512 isNaN = Vector512.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(double.PositiveInfinityBits)); + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(double.PositiveZeroBits)); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(double.NegativeInfinityBits)); + + // Determine if negative (sign bit set) + Vector512 isNegative = Vector512.LessThan(xDouble, Vector512.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector512 incremented = bits + Vector512.One; + Vector512 decremented = bits - Vector512.One; + + // Select based on sign: negative -> increment, positive -> decrement + Vector512 result = Vector512.ConditionalSelect(isNegative, incremented, decremented); + + // Handle special cases + // +0.0 -> -Epsilon + result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + + // +Infinity -> MaxValue + result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + + // NaN -> NaN (return original), -Infinity -> -Infinity (return original) + Vector512 preserveOriginal = isNaN | isNegInf; + result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } } } } 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 8af973d72b41b7..35d55c0df9e8c1 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 @@ -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.Runtime.CompilerServices; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -26,11 +27,240 @@ public static void BitIncrement(ReadOnlySpan x, Span destination) private readonly struct BitIncrementOperator : IUnaryOperator where T : IFloatingPointIeee754 { - public static bool Vectorizable => false; // TODO: Vectorize + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + public static T Invoke(T x) => T.BitIncrement(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(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + Vector128 xFloat = x.AsSingle(); + Vector128 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector128 isNaNMask = Vector128.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector128 isNaN = Vector128.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(float.NegativeInfinityBits)); + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(float.NegativeZeroBits)); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(float.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector128 isNegative = Vector128.LessThan(xFloat, Vector128.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector128 decremented = bits - Vector128.One; + Vector128 incremented = bits + Vector128.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector128 result = Vector128.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(float.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector128 preserveOriginal = isNaN | isPosInf; + result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector128 xDouble = x.AsDouble(); + Vector128 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector128 isNaNMask = Vector128.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector128 isNaN = Vector128.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(double.NegativeInfinityBits)); + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(double.NegativeZeroBits)); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(double.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector128 isNegative = Vector128.LessThan(xDouble, Vector128.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector128 decremented = bits - Vector128.One; + Vector128 incremented = bits + Vector128.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector128 result = Vector128.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(double.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector128 preserveOriginal = isNaN | isPosInf; + result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + Vector256 xFloat = x.AsSingle(); + Vector256 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector256 isNaNMask = Vector256.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector256 isNaN = Vector256.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(float.NegativeInfinityBits)); + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(float.NegativeZeroBits)); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(float.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector256 isNegative = Vector256.LessThan(xFloat, Vector256.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector256 decremented = bits - Vector256.One; + Vector256 incremented = bits + Vector256.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector256 result = Vector256.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(float.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector256 preserveOriginal = isNaN | isPosInf; + result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector256 xDouble = x.AsDouble(); + Vector256 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector256 isNaNMask = Vector256.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector256 isNaN = Vector256.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(double.NegativeInfinityBits)); + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(double.NegativeZeroBits)); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(double.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector256 isNegative = Vector256.LessThan(xDouble, Vector256.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector256 decremented = bits - Vector256.One; + Vector256 incremented = bits + Vector256.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector256 result = Vector256.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(double.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector256 preserveOriginal = isNaN | isPosInf; + result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + Vector512 xFloat = x.AsSingle(); + Vector512 bits = xFloat.AsUInt32(); + + // Create masks for special cases + Vector512 isNaNMask = Vector512.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); + Vector512 isNaN = Vector512.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(float.NegativeInfinityBits)); + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(float.NegativeZeroBits)); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(float.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector512 isNegative = Vector512.LessThan(xFloat, Vector512.Zero).AsUInt32(); + + // Compute bit incremented/decremented results + Vector512 decremented = bits - Vector512.One; + Vector512 incremented = bits + Vector512.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector512 result = Vector512.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(float.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector512 preserveOriginal = isNaN | isPosInf; + result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsSingle().As(); + } + else if (typeof(T) == typeof(double)) + { + Vector512 xDouble = x.AsDouble(); + Vector512 bits = xDouble.AsUInt64(); + + // Create masks for special cases + Vector512 isNaNMask = Vector512.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); + Vector512 isNaN = Vector512.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(double.NegativeInfinityBits)); + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(double.NegativeZeroBits)); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(double.PositiveInfinityBits)); + + // Determine if negative (sign bit set) + Vector512 isNegative = Vector512.LessThan(xDouble, Vector512.Zero).AsUInt64(); + + // Compute bit incremented/decremented results + Vector512 decremented = bits - Vector512.One; + Vector512 incremented = bits + Vector512.One; + + // Select based on sign: negative -> decrement, positive -> increment + Vector512 result = Vector512.ConditionalSelect(isNegative, decremented, incremented); + + // Handle special cases + // -0.0 -> Epsilon + result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(double.EpsilonBits), result); + + // -Infinity -> MinValue + result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + + // NaN -> NaN (return original), +Infinity -> +Infinity (return original) + Vector512 preserveOriginal = isNaN | isPosInf; + result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + + return result.AsDouble().As(); + } + + return ApplyScalar>(x); + } } } } From 7b31447f64a6c7ec68be00b75f0901da34d377be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 03:02:53 +0000 Subject: [PATCH 03/14] Fix sign bit detection and NaN masking in BitIncrement and BitDecrement Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 75 ++++++++-------- .../netcore/TensorPrimitives.BitIncrement.cs | 87 +++++++++---------- 2 files changed, 78 insertions(+), 84 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 987ad3cc8a5cb1..3e6c1299d6c816 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 @@ -40,14 +40,13 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector128 isNaNMask = Vector128.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector128 isNaN = Vector128.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(float.PositiveInfinityBits)); - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(float.PositiveZeroBits)); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(float.NegativeInfinityBits)); + Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000u)); // PositiveZeroBits + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.LessThan(xFloat, Vector128.Zero).AsUInt32(); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector128 incremented = bits + Vector128.One; @@ -75,14 +74,13 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector128 isNaNMask = Vector128.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector128 isNaN = Vector128.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(double.PositiveInfinityBits)); - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(double.PositiveZeroBits)); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(double.NegativeInfinityBits)); + Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.LessThan(xDouble, Vector128.Zero).AsUInt64(); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector128 incremented = bits + Vector128.One; @@ -105,7 +103,8 @@ public static Vector128 Invoke(Vector128 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -117,14 +116,13 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector256 isNaNMask = Vector256.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector256 isNaN = Vector256.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(float.PositiveInfinityBits)); - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(float.PositiveZeroBits)); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(float.NegativeInfinityBits)); + Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000u)); // PositiveZeroBits + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.LessThan(xFloat, Vector256.Zero).AsUInt32(); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector256 incremented = bits + Vector256.One; @@ -152,14 +150,13 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector256 isNaNMask = Vector256.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector256 isNaN = Vector256.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(double.PositiveInfinityBits)); - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(double.PositiveZeroBits)); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(double.NegativeInfinityBits)); + Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.LessThan(xDouble, Vector256.Zero).AsUInt64(); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector256 incremented = bits + Vector256.One; @@ -182,7 +179,8 @@ public static Vector256 Invoke(Vector256 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -194,14 +192,13 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector512 isNaNMask = Vector512.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector512 isNaN = Vector512.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(float.PositiveInfinityBits)); - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(float.PositiveZeroBits)); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(float.NegativeInfinityBits)); + Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000u)); // PositiveZeroBits + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.LessThan(xFloat, Vector512.Zero).AsUInt32(); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector512 incremented = bits + Vector512.One; @@ -229,14 +226,13 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector512 isNaNMask = Vector512.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector512 isNaN = Vector512.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(double.PositiveInfinityBits)); - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(double.PositiveZeroBits)); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(double.NegativeInfinityBits)); + Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.LessThan(xDouble, Vector512.Zero).AsUInt64(); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector512 incremented = bits + Vector512.One; @@ -259,7 +255,8 @@ public static Vector512 Invoke(Vector512 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } } } 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 35d55c0df9e8c1..0dad78ae7876f0 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 @@ -40,14 +40,13 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector128 isNaNMask = Vector128.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector128 isNaN = Vector128.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(float.NegativeInfinityBits)); - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(float.NegativeZeroBits)); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(float.PositiveInfinityBits)); + Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); // NegativeZeroBits + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.LessThan(xFloat, Vector128.Zero).AsUInt32(); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector128 decremented = bits - Vector128.One; @@ -58,7 +57,7 @@ public static Vector128 Invoke(Vector128 x) // Handle special cases // -0.0 -> Epsilon - result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(float.EpsilonBits), result); + result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); @@ -75,14 +74,13 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector128 isNaNMask = Vector128.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector128 isNaN = Vector128.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(double.NegativeInfinityBits)); - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(double.NegativeZeroBits)); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(double.PositiveInfinityBits)); + Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.LessThan(xDouble, Vector128.Zero).AsUInt64(); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector128 decremented = bits - Vector128.One; @@ -93,7 +91,7 @@ public static Vector128 Invoke(Vector128 x) // Handle special cases // -0.0 -> Epsilon - result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(double.EpsilonBits), result); + result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); @@ -105,7 +103,8 @@ public static Vector128 Invoke(Vector128 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -117,14 +116,13 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector256 isNaNMask = Vector256.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector256 isNaN = Vector256.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(float.NegativeInfinityBits)); - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(float.NegativeZeroBits)); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(float.PositiveInfinityBits)); + Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); // NegativeZeroBits + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.LessThan(xFloat, Vector256.Zero).AsUInt32(); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector256 decremented = bits - Vector256.One; @@ -135,7 +133,7 @@ public static Vector256 Invoke(Vector256 x) // Handle special cases // -0.0 -> Epsilon - result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(float.EpsilonBits), result); + result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); @@ -152,14 +150,13 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector256 isNaNMask = Vector256.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector256 isNaN = Vector256.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(double.NegativeInfinityBits)); - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(double.NegativeZeroBits)); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(double.PositiveInfinityBits)); + Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.LessThan(xDouble, Vector256.Zero).AsUInt64(); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector256 decremented = bits - Vector256.One; @@ -170,7 +167,7 @@ public static Vector256 Invoke(Vector256 x) // Handle special cases // -0.0 -> Epsilon - result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(double.EpsilonBits), result); + result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); @@ -182,7 +179,8 @@ public static Vector256 Invoke(Vector256 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -194,14 +192,13 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xFloat.AsUInt32(); // Create masks for special cases - Vector512 isNaNMask = Vector512.Create(BitConverter.UInt32BitsToSingle(0xFFFFFFFF)); - Vector512 isNaN = Vector512.Equals(xFloat, xFloat).AsUInt32() ^ isNaNMask.AsUInt32(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(float.NegativeInfinityBits)); - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(float.NegativeZeroBits)); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(float.PositiveInfinityBits)); + Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); // NegativeZeroBits + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.LessThan(xFloat, Vector512.Zero).AsUInt32(); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); // Compute bit incremented/decremented results Vector512 decremented = bits - Vector512.One; @@ -212,7 +209,7 @@ public static Vector512 Invoke(Vector512 x) // Handle special cases // -0.0 -> Epsilon - result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(float.EpsilonBits), result); + result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); @@ -229,14 +226,13 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xDouble.AsUInt64(); // Create masks for special cases - Vector512 isNaNMask = Vector512.Create(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF)); - Vector512 isNaN = Vector512.Equals(xDouble, xDouble).AsUInt64() ^ isNaNMask.AsUInt64(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(double.NegativeInfinityBits)); - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(double.NegativeZeroBits)); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(double.PositiveInfinityBits)); + Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.LessThan(xDouble, Vector512.Zero).AsUInt64(); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); // Compute bit incremented/decremented results Vector512 decremented = bits - Vector512.One; @@ -247,7 +243,7 @@ public static Vector512 Invoke(Vector512 x) // Handle special cases // -0.0 -> Epsilon - result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(double.EpsilonBits), result); + result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); @@ -259,7 +255,8 @@ public static Vector512 Invoke(Vector512 x) return result.AsDouble().As(); } - return ApplyScalar>(x); + // Fallback for unsupported types - should not be reached since Vectorizable returns false + throw new NotSupportedException(); } } } From 97ccc34253b62e17848e7dbf88c5282844a63ebc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:22:44 +0000 Subject: [PATCH 04/14] Refactor: Consolidate variables and move declarations closer to usage per code review Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 156 +++++++----------- .../netcore/TensorPrimitives.BitIncrement.cs | 156 +++++++----------- 2 files changed, 120 insertions(+), 192 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 3e6c1299d6c816..ce0546919e4dae 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 @@ -39,32 +39,26 @@ public static Vector128 Invoke(Vector128 x) Vector128 xFloat = x.AsSingle(); Vector128 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000u)); // PositiveZeroBits - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector128 incremented = bits + Vector128.One; - Vector128 decremented = bits - Vector128.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector128 result = Vector128.ConditionalSelect(isNegative, incremented, decremented); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits + Vector128.One, + bits - Vector128.One); // Handle special cases // +0.0 -> -Epsilon + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000u)); // PositiveZeroBits result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); // +Infinity -> MaxValue + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector128 preserveOriginal = isNaN | isNegInf; - result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits + result = Vector128.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsSingle().As(); } @@ -73,32 +67,26 @@ public static Vector128 Invoke(Vector128 x) Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector128 incremented = bits + Vector128.One; - Vector128 decremented = bits - Vector128.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector128 result = Vector128.ConditionalSelect(isNegative, incremented, decremented); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits + Vector128.One, + bits - Vector128.One); // Handle special cases // +0.0 -> -Epsilon + Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); // +Infinity -> MaxValue + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector128 preserveOriginal = isNaN | isNegInf; - result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + result = Vector128.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsDouble().As(); } @@ -115,32 +103,26 @@ public static Vector256 Invoke(Vector256 x) Vector256 xFloat = x.AsSingle(); Vector256 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000u)); // PositiveZeroBits - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector256 incremented = bits + Vector256.One; - Vector256 decremented = bits - Vector256.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector256 result = Vector256.ConditionalSelect(isNegative, incremented, decremented); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits + Vector256.One, + bits - Vector256.One); // Handle special cases // +0.0 -> -Epsilon + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000u)); // PositiveZeroBits result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); // +Infinity -> MaxValue + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector256 preserveOriginal = isNaN | isNegInf; - result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits + result = Vector256.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsSingle().As(); } @@ -149,32 +131,26 @@ public static Vector256 Invoke(Vector256 x) Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector256 incremented = bits + Vector256.One; - Vector256 decremented = bits - Vector256.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector256 result = Vector256.ConditionalSelect(isNegative, incremented, decremented); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits + Vector256.One, + bits - Vector256.One); // Handle special cases // +0.0 -> -Epsilon + Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); // +Infinity -> MaxValue + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector256 preserveOriginal = isNaN | isNegInf; - result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + result = Vector256.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsDouble().As(); } @@ -191,32 +167,26 @@ public static Vector512 Invoke(Vector512 x) Vector512 xFloat = x.AsSingle(); Vector512 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000u)); // PositiveZeroBits - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector512 incremented = bits + Vector512.One; - Vector512 decremented = bits - Vector512.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector512 result = Vector512.ConditionalSelect(isNegative, incremented, decremented); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits + Vector512.One, + bits - Vector512.One); // Handle special cases // +0.0 -> -Epsilon + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000u)); // PositiveZeroBits result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); // +Infinity -> MaxValue + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector512 preserveOriginal = isNaN | isNegInf; - result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits + result = Vector512.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsSingle().As(); } @@ -225,32 +195,26 @@ public static Vector512 Invoke(Vector512 x) Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - - // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector512 incremented = bits + Vector512.One; - Vector512 decremented = bits - Vector512.One; - // Select based on sign: negative -> increment, positive -> decrement - Vector512 result = Vector512.ConditionalSelect(isNegative, incremented, decremented); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits + Vector512.One, + bits - Vector512.One); // Handle special cases // +0.0 -> -Epsilon + Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); // +Infinity -> MaxValue + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector512 preserveOriginal = isNaN | isNegInf; - result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits + result = Vector512.ConditionalSelect(isNaN | isNegInf, bits, result); return result.AsDouble().As(); } 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 0dad78ae7876f0..92cba0e553612d 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 @@ -39,32 +39,26 @@ public static Vector128 Invoke(Vector128 x) Vector128 xFloat = x.AsSingle(); Vector128 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); // NegativeZeroBits - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector128 decremented = bits - Vector128.One; - Vector128 incremented = bits + Vector128.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector128 result = Vector128.ConditionalSelect(isNegative, decremented, incremented); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits - Vector128.One, + bits + Vector128.One); // Handle special cases // -0.0 -> Epsilon + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); // NegativeZeroBits result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector128 preserveOriginal = isNaN | isPosInf; - result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits + result = Vector128.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsSingle().As(); } @@ -73,32 +67,26 @@ public static Vector128 Invoke(Vector128 x) Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector128 decremented = bits - Vector128.One; - Vector128 incremented = bits + Vector128.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector128 result = Vector128.ConditionalSelect(isNegative, decremented, incremented); + Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); + Vector128 result = Vector128.ConditionalSelect( + isNegative, + bits - Vector128.One, + bits + Vector128.One); // Handle special cases // -0.0 -> Epsilon + Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue + Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector128 preserveOriginal = isNaN | isPosInf; - result = Vector128.ConditionalSelect(preserveOriginal, bits, result); + Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); + Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + result = Vector128.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsDouble().As(); } @@ -115,32 +103,26 @@ public static Vector256 Invoke(Vector256 x) Vector256 xFloat = x.AsSingle(); Vector256 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); // NegativeZeroBits - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector256 decremented = bits - Vector256.One; - Vector256 incremented = bits + Vector256.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector256 result = Vector256.ConditionalSelect(isNegative, decremented, incremented); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits - Vector256.One, + bits + Vector256.One); // Handle special cases // -0.0 -> Epsilon + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); // NegativeZeroBits result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector256 preserveOriginal = isNaN | isPosInf; - result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits + result = Vector256.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsSingle().As(); } @@ -149,32 +131,26 @@ public static Vector256 Invoke(Vector256 x) Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector256 decremented = bits - Vector256.One; - Vector256 incremented = bits + Vector256.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector256 result = Vector256.ConditionalSelect(isNegative, decremented, incremented); + Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); + Vector256 result = Vector256.ConditionalSelect( + isNegative, + bits - Vector256.One, + bits + Vector256.One); // Handle special cases // -0.0 -> Epsilon + Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue + Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector256 preserveOriginal = isNaN | isPosInf; - result = Vector256.ConditionalSelect(preserveOriginal, bits, result); + Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); + Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + result = Vector256.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsDouble().As(); } @@ -191,32 +167,26 @@ public static Vector512 Invoke(Vector512 x) Vector512 xFloat = x.AsSingle(); Vector512 bits = xFloat.AsUInt32(); - // Create masks for special cases - Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); // NegativeZeroBits - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); - - // Compute bit incremented/decremented results - Vector512 decremented = bits - Vector512.One; - Vector512 incremented = bits + Vector512.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector512 result = Vector512.ConditionalSelect(isNegative, decremented, incremented); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits - Vector512.One, + bits + Vector512.One); // Handle special cases // -0.0 -> Epsilon + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); // NegativeZeroBits result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0001u), result); // EpsilonBits // -Infinity -> MinValue + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector512 preserveOriginal = isNaN | isPosInf; - result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits + result = Vector512.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsSingle().As(); } @@ -225,32 +195,26 @@ public static Vector512 Invoke(Vector512 x) Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); - // Create masks for special cases - Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - - // Determine if negative (sign bit set) - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); - - // Compute bit incremented/decremented results - Vector512 decremented = bits - Vector512.One; - Vector512 incremented = bits + Vector512.One; - // Select based on sign: negative -> decrement, positive -> increment - Vector512 result = Vector512.ConditionalSelect(isNegative, decremented, incremented); + Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); + Vector512 result = Vector512.ConditionalSelect( + isNegative, + bits - Vector512.One, + bits + Vector512.One); // Handle special cases // -0.0 -> Epsilon + Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits // -Infinity -> MinValue + Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector512 preserveOriginal = isNaN | isPosInf; - result = Vector512.ConditionalSelect(preserveOriginal, bits, result); + Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); + Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits + result = Vector512.ConditionalSelect(isNaN | isPosInf, bits, result); return result.AsDouble().As(); } From 40e6702008f3bdea0b5b7709b46534cc1cd7fc30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:37:30 +0000 Subject: [PATCH 05/14] Add Half type support via TryUnaryInvokeHalfAsInt16 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 9 ++++++++- .../Tensors/netcore/TensorPrimitives.BitIncrement.cs | 9 ++++++++- 2 files changed, 16 insertions(+), 2 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 ce0546919e4dae..da0a4739351d1c 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) && TryUnaryInvokeHalfAsInt16>(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitDecrement(x) private readonly struct BitDecrementOperator : IUnaryOperator 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 92cba0e553612d..cefad5e70e8441 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) && TryUnaryInvokeHalfAsInt16>(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitIncrement(x) private readonly struct BitIncrementOperator : IUnaryOperator From 1d4810c490768039dfab0c46f4aaf108fa26fdcf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 12:07:03 +0000 Subject: [PATCH 06/14] Change else if to if and add blank lines for readability Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 9 ++++++--- .../Tensors/netcore/TensorPrimitives.BitIncrement.cs | 9 ++++++--- 2 files changed, 12 insertions(+), 6 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 da0a4739351d1c..d183a52c6598bd 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 @@ -69,7 +69,8 @@ public static Vector128 Invoke(Vector128 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); @@ -133,7 +134,8 @@ public static Vector256 Invoke(Vector256 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); @@ -197,7 +199,8 @@ public static Vector512 Invoke(Vector512 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); 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 cefad5e70e8441..91cae3990b75e4 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 @@ -69,7 +69,8 @@ public static Vector128 Invoke(Vector128 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); @@ -133,7 +134,8 @@ public static Vector256 Invoke(Vector256 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); @@ -197,7 +199,8 @@ public static Vector512 Invoke(Vector512 x) return result.AsSingle().As(); } - else if (typeof(T) == typeof(double)) + + if (typeof(T) == typeof(double)) { Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); From 72a7b3c21a2f119fa5e6aef0b1c5245956acb512 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:00:31 +0000 Subject: [PATCH 07/14] Use Vector.IsNegative() API instead of manual sign bit checking Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 12 ++++++------ .../Tensors/netcore/TensorPrimitives.BitIncrement.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 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 d183a52c6598bd..73f52e04c86b01 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 @@ -47,7 +47,7 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xFloat.AsUInt32(); // Select based on sign: negative -> increment, positive -> decrement - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); + Vector128 isNegative = Vector128.IsNegative(xFloat).AsUInt32(); Vector128 result = Vector128.ConditionalSelect( isNegative, bits + Vector128.One, @@ -76,7 +76,7 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xDouble.AsUInt64(); // Select based on sign: negative -> increment, positive -> decrement - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); + Vector128 isNegative = Vector128.IsNegative(xDouble).AsUInt64(); Vector128 result = Vector128.ConditionalSelect( isNegative, bits + Vector128.One, @@ -112,7 +112,7 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xFloat.AsUInt32(); // Select based on sign: negative -> increment, positive -> decrement - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); + Vector256 isNegative = Vector256.IsNegative(xFloat).AsUInt32(); Vector256 result = Vector256.ConditionalSelect( isNegative, bits + Vector256.One, @@ -141,7 +141,7 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xDouble.AsUInt64(); // Select based on sign: negative -> increment, positive -> decrement - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); + Vector256 isNegative = Vector256.IsNegative(xDouble).AsUInt64(); Vector256 result = Vector256.ConditionalSelect( isNegative, bits + Vector256.One, @@ -177,7 +177,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xFloat.AsUInt32(); // Select based on sign: negative -> increment, positive -> decrement - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); + Vector512 isNegative = Vector512.IsNegative(xFloat).AsUInt32(); Vector512 result = Vector512.ConditionalSelect( isNegative, bits + Vector512.One, @@ -206,7 +206,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xDouble.AsUInt64(); // Select based on sign: negative -> increment, positive -> decrement - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); + Vector512 isNegative = Vector512.IsNegative(xDouble).AsUInt64(); Vector512 result = Vector512.ConditionalSelect( isNegative, bits + Vector512.One, 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 91cae3990b75e4..2890590583e8e8 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 @@ -47,7 +47,7 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xFloat.AsUInt32(); // Select based on sign: negative -> decrement, positive -> increment - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000u), Vector128.Create(0x8000_0000u)); + Vector128 isNegative = Vector128.IsNegative(xFloat).AsUInt32(); Vector128 result = Vector128.ConditionalSelect( isNegative, bits - Vector128.One, @@ -76,7 +76,7 @@ public static Vector128 Invoke(Vector128 x) Vector128 bits = xDouble.AsUInt64(); // Select based on sign: negative -> decrement, positive -> increment - Vector128 isNegative = Vector128.Equals(bits & Vector128.Create(0x8000_0000_0000_0000ul), Vector128.Create(0x8000_0000_0000_0000ul)); + Vector128 isNegative = Vector128.IsNegative(xDouble).AsUInt64(); Vector128 result = Vector128.ConditionalSelect( isNegative, bits - Vector128.One, @@ -112,7 +112,7 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xFloat.AsUInt32(); // Select based on sign: negative -> decrement, positive -> increment - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000u), Vector256.Create(0x8000_0000u)); + Vector256 isNegative = Vector256.IsNegative(xFloat).AsUInt32(); Vector256 result = Vector256.ConditionalSelect( isNegative, bits - Vector256.One, @@ -141,7 +141,7 @@ public static Vector256 Invoke(Vector256 x) Vector256 bits = xDouble.AsUInt64(); // Select based on sign: negative -> decrement, positive -> increment - Vector256 isNegative = Vector256.Equals(bits & Vector256.Create(0x8000_0000_0000_0000ul), Vector256.Create(0x8000_0000_0000_0000ul)); + Vector256 isNegative = Vector256.IsNegative(xDouble).AsUInt64(); Vector256 result = Vector256.ConditionalSelect( isNegative, bits - Vector256.One, @@ -177,7 +177,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xFloat.AsUInt32(); // Select based on sign: negative -> decrement, positive -> increment - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000u), Vector512.Create(0x8000_0000u)); + Vector512 isNegative = Vector512.IsNegative(xFloat).AsUInt32(); Vector512 result = Vector512.ConditionalSelect( isNegative, bits - Vector512.One, @@ -206,7 +206,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 bits = xDouble.AsUInt64(); // Select based on sign: negative -> decrement, positive -> increment - Vector512 isNegative = Vector512.Equals(bits & Vector512.Create(0x8000_0000_0000_0000ul), Vector512.Create(0x8000_0000_0000_0000ul)); + Vector512 isNegative = Vector512.IsNegative(xDouble).AsUInt64(); Vector512 result = Vector512.ConditionalSelect( isNegative, bits - Vector512.One, From bbe89c3ce55d5928089aec83a8f804bc80c81b74 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:16:27 +0000 Subject: [PATCH 08/14] Remove Half type support due to performance regression Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 9 +-------- .../Tensors/netcore/TensorPrimitives.BitIncrement.cs | 9 +-------- 2 files changed, 2 insertions(+), 16 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 73f52e04c86b01..44615c9d606e9d 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,15 +20,8 @@ public static partial class TensorPrimitives /// /// public static void BitDecrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 - { - if (typeof(T) == typeof(Half) && TryUnaryInvokeHalfAsInt16>(x, destination)) - { - return; - } - + where T : IFloatingPointIeee754 => InvokeSpanIntoSpan>(x, destination); - } /// T.BitDecrement(x) private readonly struct BitDecrementOperator : IUnaryOperator 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 2890590583e8e8..c900da2d30b821 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,15 +20,8 @@ public static partial class TensorPrimitives /// /// public static void BitIncrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 - { - if (typeof(T) == typeof(Half) && TryUnaryInvokeHalfAsInt16>(x, destination)) - { - return; - } - + where T : IFloatingPointIeee754 => InvokeSpanIntoSpan>(x, destination); - } /// T.BitIncrement(x) private readonly struct BitIncrementOperator : IUnaryOperator From 0522459d1105fde626a95ce4974e10ddeb657d9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:44:23 +0000 Subject: [PATCH 09/14] Optimize special case handling with single ConditionalSelect per @tannergooding Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 156 ++++++++---------- .../netcore/TensorPrimitives.BitIncrement.cs | 156 ++++++++---------- 2 files changed, 132 insertions(+), 180 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 44615c9d606e9d..7a80e596680763 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 @@ -39,28 +39,24 @@ public static Vector128 Invoke(Vector128 x) Vector128 xFloat = x.AsSingle(); Vector128 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> increment, positive -> decrement - Vector128 isNegative = Vector128.IsNegative(xFloat).AsUInt32(); + // General case: negative -> increment, positive -> decrement Vector128 result = Vector128.ConditionalSelect( - isNegative, + Vector128.IsNegative(xFloat).AsUInt32(), bits + Vector128.One, bits - Vector128.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000u)); // PositiveZeroBits - result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + // Handle special cases with a single conditional select + Vector128 isPositiveZero = Vector128.IsZero(xFloat.AsUInt32()); + Vector128 specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + Vector128 isPosInf = Vector128.IsPositiveInfinity(xFloat).AsUInt32(); + specialValue |= Vector128.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector128.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector128 isNaNOrNegInf = (Vector128.IsNaN(xFloat) | Vector128.IsNegativeInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrNegInf; - return result.AsSingle().As(); + Vector128 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -68,28 +64,24 @@ public static Vector128 Invoke(Vector128 x) Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> increment, positive -> decrement - Vector128 isNegative = Vector128.IsNegative(xDouble).AsUInt64(); + // General case: negative -> increment, positive -> decrement Vector128 result = Vector128.ConditionalSelect( - isNegative, + Vector128.IsNegative(xDouble).AsUInt64(), bits + Vector128.One, bits - Vector128.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector128 isPosZero = Vector128.Equals(bits, Vector128.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - result = Vector128.ConditionalSelect(isPosZero, Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + // Handle special cases with a single conditional select + Vector128 isPositiveZero = Vector128.IsZero(xDouble.AsUInt64()); + Vector128 specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector128.ConditionalSelect(isPosInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + Vector128 isPosInf = Vector128.IsPositiveInfinity(xDouble).AsUInt64(); + specialValue |= Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector128.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector128 isNaNOrNegInf = (Vector128.IsNaN(xDouble) | Vector128.IsNegativeInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrNegInf; - return result.AsDouble().As(); + Vector128 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false @@ -104,28 +96,24 @@ public static Vector256 Invoke(Vector256 x) Vector256 xFloat = x.AsSingle(); Vector256 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> increment, positive -> decrement - Vector256 isNegative = Vector256.IsNegative(xFloat).AsUInt32(); + // General case: negative -> increment, positive -> decrement Vector256 result = Vector256.ConditionalSelect( - isNegative, + Vector256.IsNegative(xFloat).AsUInt32(), bits + Vector256.One, bits - Vector256.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000u)); // PositiveZeroBits - result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + // Handle special cases with a single conditional select + Vector256 isPositiveZero = Vector256.IsZero(xFloat.AsUInt32()); + Vector256 specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + Vector256 isPosInf = Vector256.IsPositiveInfinity(xFloat).AsUInt32(); + specialValue |= Vector256.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector256.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector256 isNaNOrNegInf = (Vector256.IsNaN(xFloat) | Vector256.IsNegativeInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrNegInf; - return result.AsSingle().As(); + Vector256 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -133,28 +121,24 @@ public static Vector256 Invoke(Vector256 x) Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> increment, positive -> decrement - Vector256 isNegative = Vector256.IsNegative(xDouble).AsUInt64(); + // General case: negative -> increment, positive -> decrement Vector256 result = Vector256.ConditionalSelect( - isNegative, + Vector256.IsNegative(xDouble).AsUInt64(), bits + Vector256.One, bits - Vector256.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector256 isPosZero = Vector256.Equals(bits, Vector256.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - result = Vector256.ConditionalSelect(isPosZero, Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + // Handle special cases with a single conditional select + Vector256 isPositiveZero = Vector256.IsZero(xDouble.AsUInt64()); + Vector256 specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector256.ConditionalSelect(isPosInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + Vector256 isPosInf = Vector256.IsPositiveInfinity(xDouble).AsUInt64(); + specialValue |= Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector256.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector256 isNaNOrNegInf = (Vector256.IsNaN(xDouble) | Vector256.IsNegativeInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrNegInf; - return result.AsDouble().As(); + Vector256 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false @@ -169,28 +153,24 @@ public static Vector512 Invoke(Vector512 x) Vector512 xFloat = x.AsSingle(); Vector512 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> increment, positive -> decrement - Vector512 isNegative = Vector512.IsNegative(xFloat).AsUInt32(); + // General case: negative -> increment, positive -> decrement Vector512 result = Vector512.ConditionalSelect( - isNegative, + Vector512.IsNegative(xFloat).AsUInt32(), bits + Vector512.One, bits - Vector512.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000u)); // PositiveZeroBits - result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)), result); + // Handle special cases with a single conditional select + Vector512 isPositiveZero = Vector512.IsZero(xFloat.AsUInt32()); + Vector512 specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)), result); + Vector512 isPosInf = Vector512.IsPositiveInfinity(xFloat).AsUInt32(); + specialValue |= Vector512.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector512.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector512 isNaNOrNegInf = (Vector512.IsNaN(xFloat) | Vector512.IsNegativeInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrNegInf; - return result.AsSingle().As(); + Vector512 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -198,28 +178,24 @@ public static Vector512 Invoke(Vector512 x) Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> increment, positive -> decrement - Vector512 isNegative = Vector512.IsNegative(xDouble).AsUInt64(); + // General case: negative -> increment, positive -> decrement Vector512 result = Vector512.ConditionalSelect( - isNegative, + Vector512.IsNegative(xDouble).AsUInt64(), bits + Vector512.One, bits - Vector512.One); - // Handle special cases - // +0.0 -> -Epsilon - Vector512 isPosZero = Vector512.Equals(bits, Vector512.Create(0x0000_0000_0000_0000ul)); // PositiveZeroBits - result = Vector512.ConditionalSelect(isPosZero, Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)), result); + // Handle special cases with a single conditional select + Vector512 isPositiveZero = Vector512.IsZero(xDouble.AsUInt64()); + Vector512 specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - // +Infinity -> MaxValue - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector512.ConditionalSelect(isPosInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)), result); + Vector512 isPosInf = Vector512.IsPositiveInfinity(xDouble).AsUInt64(); + specialValue |= Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - // NaN -> NaN (return original), -Infinity -> -Infinity (return original) - Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector512.ConditionalSelect(isNaN | isNegInf, bits, result); + Vector512 isNaNOrNegInf = (Vector512.IsNaN(xDouble) | Vector512.IsNegativeInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrNegInf; - return result.AsDouble().As(); + Vector512 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false 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 c900da2d30b821..6f437345ac7434 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 @@ -39,28 +39,24 @@ public static Vector128 Invoke(Vector128 x) Vector128 xFloat = x.AsSingle(); Vector128 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> decrement, positive -> increment - Vector128 isNegative = Vector128.IsNegative(xFloat).AsUInt32(); + // General case: negative -> decrement, positive -> increment Vector128 result = Vector128.ConditionalSelect( - isNegative, + Vector128.IsNegative(xFloat).AsUInt32(), bits - Vector128.One, bits + Vector128.One); - // Handle special cases - // -0.0 -> Epsilon - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); // NegativeZeroBits - result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0001u), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); + Vector128 specialValue = Vector128.Create(0x0000_0001u) & isNegativeZero; - // -Infinity -> MinValue - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + Vector128 isNegInf = Vector128.IsNegativeInfinity(xFloat).AsUInt32(); + specialValue |= Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector128 isNaN = ~Vector128.Equals(xFloat, xFloat).AsUInt32(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector128.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector128 isNaNOrPosInf = (Vector128.IsNaN(xFloat) | Vector128.IsPositiveInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrPosInf; - return result.AsSingle().As(); + Vector128 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -68,28 +64,24 @@ public static Vector128 Invoke(Vector128 x) Vector128 xDouble = x.AsDouble(); Vector128 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> decrement, positive -> increment - Vector128 isNegative = Vector128.IsNegative(xDouble).AsUInt64(); + // General case: negative -> decrement, positive -> increment Vector128 result = Vector128.ConditionalSelect( - isNegative, + Vector128.IsNegative(xDouble).AsUInt64(), bits - Vector128.One, bits + Vector128.One); - // Handle special cases - // -0.0 -> Epsilon - Vector128 isNegZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - result = Vector128.ConditionalSelect(isNegZero, Vector128.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); + Vector128 specialValue = Vector128.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - // -Infinity -> MinValue - Vector128 isNegInf = Vector128.Equals(bits, Vector128.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector128.ConditionalSelect(isNegInf, Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + Vector128 isNegInf = Vector128.IsNegativeInfinity(xDouble).AsUInt64(); + specialValue |= Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector128 isNaN = ~Vector128.Equals(xDouble, xDouble).AsUInt64(); - Vector128 isPosInf = Vector128.Equals(bits, Vector128.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector128.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector128 isNaNOrPosInf = (Vector128.IsNaN(xDouble) | Vector128.IsPositiveInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrPosInf; - return result.AsDouble().As(); + Vector128 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false @@ -104,28 +96,24 @@ public static Vector256 Invoke(Vector256 x) Vector256 xFloat = x.AsSingle(); Vector256 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> decrement, positive -> increment - Vector256 isNegative = Vector256.IsNegative(xFloat).AsUInt32(); + // General case: negative -> decrement, positive -> increment Vector256 result = Vector256.ConditionalSelect( - isNegative, + Vector256.IsNegative(xFloat).AsUInt32(), bits - Vector256.One, bits + Vector256.One); - // Handle special cases - // -0.0 -> Epsilon - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); // NegativeZeroBits - result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0001u), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); + Vector256 specialValue = Vector256.Create(0x0000_0001u) & isNegativeZero; - // -Infinity -> MinValue - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + Vector256 isNegInf = Vector256.IsNegativeInfinity(xFloat).AsUInt32(); + specialValue |= Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector256 isNaN = ~Vector256.Equals(xFloat, xFloat).AsUInt32(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector256.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector256 isNaNOrPosInf = (Vector256.IsNaN(xFloat) | Vector256.IsPositiveInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrPosInf; - return result.AsSingle().As(); + Vector256 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -133,28 +121,24 @@ public static Vector256 Invoke(Vector256 x) Vector256 xDouble = x.AsDouble(); Vector256 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> decrement, positive -> increment - Vector256 isNegative = Vector256.IsNegative(xDouble).AsUInt64(); + // General case: negative -> decrement, positive -> increment Vector256 result = Vector256.ConditionalSelect( - isNegative, + Vector256.IsNegative(xDouble).AsUInt64(), bits - Vector256.One, bits + Vector256.One); - // Handle special cases - // -0.0 -> Epsilon - Vector256 isNegZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - result = Vector256.ConditionalSelect(isNegZero, Vector256.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); + Vector256 specialValue = Vector256.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - // -Infinity -> MinValue - Vector256 isNegInf = Vector256.Equals(bits, Vector256.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector256.ConditionalSelect(isNegInf, Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + Vector256 isNegInf = Vector256.IsNegativeInfinity(xDouble).AsUInt64(); + specialValue |= Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector256 isNaN = ~Vector256.Equals(xDouble, xDouble).AsUInt64(); - Vector256 isPosInf = Vector256.Equals(bits, Vector256.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector256.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector256 isNaNOrPosInf = (Vector256.IsNaN(xDouble) | Vector256.IsPositiveInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrPosInf; - return result.AsDouble().As(); + Vector256 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false @@ -169,28 +153,24 @@ public static Vector512 Invoke(Vector512 x) Vector512 xFloat = x.AsSingle(); Vector512 bits = xFloat.AsUInt32(); - // Select based on sign: negative -> decrement, positive -> increment - Vector512 isNegative = Vector512.IsNegative(xFloat).AsUInt32(); + // General case: negative -> decrement, positive -> increment Vector512 result = Vector512.ConditionalSelect( - isNegative, + Vector512.IsNegative(xFloat).AsUInt32(), bits - Vector512.One, bits + Vector512.One); - // Handle special cases - // -0.0 -> Epsilon - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); // NegativeZeroBits - result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0001u), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); + Vector512 specialValue = Vector512.Create(0x0000_0001u) & isNegativeZero; - // -Infinity -> MinValue - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFF80_0000u)); // NegativeInfinityBits - result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)), result); + Vector512 isNegInf = Vector512.IsNegativeInfinity(xFloat).AsUInt32(); + specialValue |= Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector512 isNaN = ~Vector512.Equals(xFloat, xFloat).AsUInt32(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7F80_0000u)); // PositiveInfinityBits - result = Vector512.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector512 isNaNOrPosInf = (Vector512.IsNaN(xFloat) | Vector512.IsPositiveInfinity(xFloat)).AsUInt32(); + specialValue |= bits & isNaNOrPosInf; - return result.AsSingle().As(); + Vector512 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } if (typeof(T) == typeof(double)) @@ -198,28 +178,24 @@ public static Vector512 Invoke(Vector512 x) Vector512 xDouble = x.AsDouble(); Vector512 bits = xDouble.AsUInt64(); - // Select based on sign: negative -> decrement, positive -> increment - Vector512 isNegative = Vector512.IsNegative(xDouble).AsUInt64(); + // General case: negative -> decrement, positive -> increment Vector512 result = Vector512.ConditionalSelect( - isNegative, + Vector512.IsNegative(xDouble).AsUInt64(), bits - Vector512.One, bits + Vector512.One); - // Handle special cases - // -0.0 -> Epsilon - Vector512 isNegZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); // NegativeZeroBits - result = Vector512.ConditionalSelect(isNegZero, Vector512.Create(0x0000_0000_0000_0001ul), result); // EpsilonBits + // Handle special cases with a single conditional select + Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); + Vector512 specialValue = Vector512.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - // -Infinity -> MinValue - Vector512 isNegInf = Vector512.Equals(bits, Vector512.Create(0xFFF0_0000_0000_0000ul)); // NegativeInfinityBits - result = Vector512.ConditionalSelect(isNegInf, Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)), result); + Vector512 isNegInf = Vector512.IsNegativeInfinity(xDouble).AsUInt64(); + specialValue |= Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - // NaN -> NaN (return original), +Infinity -> +Infinity (return original) - Vector512 isNaN = ~Vector512.Equals(xDouble, xDouble).AsUInt64(); - Vector512 isPosInf = Vector512.Equals(bits, Vector512.Create(0x7FF0_0000_0000_0000ul)); // PositiveInfinityBits - result = Vector512.ConditionalSelect(isNaN | isPosInf, bits, result); + Vector512 isNaNOrPosInf = (Vector512.IsNaN(xDouble) | Vector512.IsPositiveInfinity(xDouble)).AsUInt64(); + specialValue |= bits & isNaNOrPosInf; - return result.AsDouble().As(); + Vector512 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } // Fallback for unsupported types - should not be reached since Vectorizable returns false From 1c6bb244b1ddc5d198fbd7bb98bd73db4f911589 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:47:20 +0000 Subject: [PATCH 10/14] Fix IsZero to operate on float/double vectors instead of uint/ulong Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 7a80e596680763..4c351ff1654e77 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 @@ -46,7 +46,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xFloat.AsUInt32()); + Vector128 isPositiveZero = Vector128.IsZero(xFloat).AsUInt32(); Vector128 specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector128 isPosInf = Vector128.IsPositiveInfinity(xFloat).AsUInt32(); @@ -71,7 +71,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xDouble.AsUInt64()); + Vector128 isPositiveZero = Vector128.IsZero(xDouble).AsUInt64(); Vector128 specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector128 isPosInf = Vector128.IsPositiveInfinity(xDouble).AsUInt64(); @@ -103,7 +103,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xFloat.AsUInt32()); + Vector256 isPositiveZero = Vector256.IsZero(xFloat).AsUInt32(); Vector256 specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector256 isPosInf = Vector256.IsPositiveInfinity(xFloat).AsUInt32(); @@ -128,7 +128,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xDouble.AsUInt64()); + Vector256 isPositiveZero = Vector256.IsZero(xDouble).AsUInt64(); Vector256 specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector256 isPosInf = Vector256.IsPositiveInfinity(xDouble).AsUInt64(); @@ -160,7 +160,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xFloat.AsUInt32()); + Vector512 isPositiveZero = Vector512.IsZero(xFloat).AsUInt32(); Vector512 specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector512 isPosInf = Vector512.IsPositiveInfinity(xFloat).AsUInt32(); @@ -185,7 +185,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xDouble.AsUInt64()); + Vector512 isPositiveZero = Vector512.IsZero(xDouble).AsUInt64(); Vector512 specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector512 isPosInf = Vector512.IsPositiveInfinity(xDouble).AsUInt64(); From a1f645eabd594d45fdc3ef2ed2945162f51d8295 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:05:38 +0000 Subject: [PATCH 11/14] Remove redundant infinity handlers - already handled by general case Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 30 ++++--------------- .../netcore/TensorPrimitives.BitIncrement.cs | 30 ++++--------------- 2 files changed, 12 insertions(+), 48 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 4c351ff1654e77..2504c0375592be 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 @@ -49,13 +49,10 @@ public static Vector128 Invoke(Vector128 x) Vector128 isPositiveZero = Vector128.IsZero(xFloat).AsUInt32(); Vector128 specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - Vector128 isPosInf = Vector128.IsPositiveInfinity(xFloat).AsUInt32(); - specialValue |= Vector128.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - Vector128 isNaNOrNegInf = (Vector128.IsNaN(xFloat) | Vector128.IsNegativeInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrNegInf; - Vector128 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector128 specialMask = isPositiveZero | isNaNOrNegInf; return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -74,13 +71,10 @@ public static Vector128 Invoke(Vector128 x) Vector128 isPositiveZero = Vector128.IsZero(xDouble).AsUInt64(); Vector128 specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - Vector128 isPosInf = Vector128.IsPositiveInfinity(xDouble).AsUInt64(); - specialValue |= Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - Vector128 isNaNOrNegInf = (Vector128.IsNaN(xDouble) | Vector128.IsNegativeInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrNegInf; - Vector128 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector128 specialMask = isPositiveZero | isNaNOrNegInf; return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } @@ -106,13 +100,10 @@ public static Vector256 Invoke(Vector256 x) Vector256 isPositiveZero = Vector256.IsZero(xFloat).AsUInt32(); Vector256 specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - Vector256 isPosInf = Vector256.IsPositiveInfinity(xFloat).AsUInt32(); - specialValue |= Vector256.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - Vector256 isNaNOrNegInf = (Vector256.IsNaN(xFloat) | Vector256.IsNegativeInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrNegInf; - Vector256 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector256 specialMask = isPositiveZero | isNaNOrNegInf; return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -131,13 +122,10 @@ public static Vector256 Invoke(Vector256 x) Vector256 isPositiveZero = Vector256.IsZero(xDouble).AsUInt64(); Vector256 specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - Vector256 isPosInf = Vector256.IsPositiveInfinity(xDouble).AsUInt64(); - specialValue |= Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - Vector256 isNaNOrNegInf = (Vector256.IsNaN(xDouble) | Vector256.IsNegativeInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrNegInf; - Vector256 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector256 specialMask = isPositiveZero | isNaNOrNegInf; return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } @@ -163,13 +151,10 @@ public static Vector512 Invoke(Vector512 x) Vector512 isPositiveZero = Vector512.IsZero(xFloat).AsUInt32(); Vector512 specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; - Vector512 isPosInf = Vector512.IsPositiveInfinity(xFloat).AsUInt32(); - specialValue |= Vector512.Create(BitConverter.SingleToUInt32Bits(float.MaxValue)) & isPosInf; - Vector512 isNaNOrNegInf = (Vector512.IsNaN(xFloat) | Vector512.IsNegativeInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrNegInf; - Vector512 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector512 specialMask = isPositiveZero | isNaNOrNegInf; return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -188,13 +173,10 @@ public static Vector512 Invoke(Vector512 x) Vector512 isPositiveZero = Vector512.IsZero(xDouble).AsUInt64(); Vector512 specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; - Vector512 isPosInf = Vector512.IsPositiveInfinity(xDouble).AsUInt64(); - specialValue |= Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MaxValue)) & isPosInf; - Vector512 isNaNOrNegInf = (Vector512.IsNaN(xDouble) | Vector512.IsNegativeInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrNegInf; - Vector512 specialMask = isPositiveZero | isPosInf | isNaNOrNegInf; + Vector512 specialMask = isPositiveZero | isNaNOrNegInf; return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } 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 6f437345ac7434..361af7af06576b 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 @@ -49,13 +49,10 @@ public static Vector128 Invoke(Vector128 x) Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u)); Vector128 specialValue = Vector128.Create(0x0000_0001u) & isNegativeZero; - Vector128 isNegInf = Vector128.IsNegativeInfinity(xFloat).AsUInt32(); - specialValue |= Vector128.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - Vector128 isNaNOrPosInf = (Vector128.IsNaN(xFloat) | Vector128.IsPositiveInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrPosInf; - Vector128 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector128 specialMask = isNegativeZero | isNaNOrPosInf; return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -74,13 +71,10 @@ public static Vector128 Invoke(Vector128 x) Vector128 isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul)); Vector128 specialValue = Vector128.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - Vector128 isNegInf = Vector128.IsNegativeInfinity(xDouble).AsUInt64(); - specialValue |= Vector128.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - Vector128 isNaNOrPosInf = (Vector128.IsNaN(xDouble) | Vector128.IsPositiveInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrPosInf; - Vector128 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector128 specialMask = isNegativeZero | isNaNOrPosInf; return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } @@ -106,13 +100,10 @@ public static Vector256 Invoke(Vector256 x) Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u)); Vector256 specialValue = Vector256.Create(0x0000_0001u) & isNegativeZero; - Vector256 isNegInf = Vector256.IsNegativeInfinity(xFloat).AsUInt32(); - specialValue |= Vector256.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - Vector256 isNaNOrPosInf = (Vector256.IsNaN(xFloat) | Vector256.IsPositiveInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrPosInf; - Vector256 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector256 specialMask = isNegativeZero | isNaNOrPosInf; return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -131,13 +122,10 @@ public static Vector256 Invoke(Vector256 x) Vector256 isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul)); Vector256 specialValue = Vector256.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - Vector256 isNegInf = Vector256.IsNegativeInfinity(xDouble).AsUInt64(); - specialValue |= Vector256.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - Vector256 isNaNOrPosInf = (Vector256.IsNaN(xDouble) | Vector256.IsPositiveInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrPosInf; - Vector256 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector256 specialMask = isNegativeZero | isNaNOrPosInf; return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } @@ -163,13 +151,10 @@ public static Vector512 Invoke(Vector512 x) Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u)); Vector512 specialValue = Vector512.Create(0x0000_0001u) & isNegativeZero; - Vector512 isNegInf = Vector512.IsNegativeInfinity(xFloat).AsUInt32(); - specialValue |= Vector512.Create(BitConverter.SingleToUInt32Bits(float.MinValue)) & isNegInf; - Vector512 isNaNOrPosInf = (Vector512.IsNaN(xFloat) | Vector512.IsPositiveInfinity(xFloat)).AsUInt32(); specialValue |= bits & isNaNOrPosInf; - Vector512 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector512 specialMask = isNegativeZero | isNaNOrPosInf; return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As(); } @@ -188,13 +173,10 @@ public static Vector512 Invoke(Vector512 x) Vector512 isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul)); Vector512 specialValue = Vector512.Create(0x0000_0000_0000_0001ul) & isNegativeZero; - Vector512 isNegInf = Vector512.IsNegativeInfinity(xDouble).AsUInt64(); - specialValue |= Vector512.Create(BitConverter.DoubleToUInt64Bits(double.MinValue)) & isNegInf; - Vector512 isNaNOrPosInf = (Vector512.IsNaN(xDouble) | Vector512.IsPositiveInfinity(xDouble)).AsUInt64(); specialValue |= bits & isNaNOrPosInf; - Vector512 specialMask = isNegativeZero | isNegInf | isNaNOrPosInf; + Vector512 specialMask = isNegativeZero | isNaNOrPosInf; return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As(); } From 3ec0153107facfe35e57ee6dfb496ac572d0d26e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:20:11 +0000 Subject: [PATCH 12/14] Re-add Half type support via TryUnaryInvokeHalfAsInt16 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 9 ++++++++- .../Tensors/netcore/TensorPrimitives.BitIncrement.cs | 9 ++++++++- 2 files changed, 16 insertions(+), 2 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..c6cd5160b61d7b 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) && TryUnaryInvokeHalfAsInt16>(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitDecrement(x) private readonly struct BitDecrementOperator : IUnaryOperator 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..41b9966f15ed7c 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) && TryUnaryInvokeHalfAsInt16>(x, destination)) + { + return; + } + InvokeSpanIntoSpan>(x, destination); + } /// T.BitIncrement(x) private readonly struct BitIncrementOperator : IUnaryOperator From 4b3b88425a10f473bd78f18147fa921ab1cf71ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:04:15 +0000 Subject: [PATCH 13/14] Optimize IsZero check to use bitwise comparison for positive zero only Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Tensors/netcore/TensorPrimitives.BitDecrement.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 c6cd5160b61d7b..20592bf9902bda 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 @@ -53,7 +53,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xFloat).AsUInt32(); + Vector128 isPositiveZero = Vector128.IsZero(xFloat.AsUInt32()); Vector128 specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector128 isNaNOrNegInf = (Vector128.IsNaN(xFloat) | Vector128.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -75,7 +75,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xDouble).AsUInt64(); + Vector128 isPositiveZero = Vector128.IsZero(xDouble.AsUInt64()); Vector128 specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector128 isNaNOrNegInf = (Vector128.IsNaN(xDouble) | Vector128.IsNegativeInfinity(xDouble)).AsUInt64(); @@ -104,7 +104,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xFloat).AsUInt32(); + Vector256 isPositiveZero = Vector256.IsZero(xFloat.AsUInt32()); Vector256 specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector256 isNaNOrNegInf = (Vector256.IsNaN(xFloat) | Vector256.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -126,7 +126,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xDouble).AsUInt64(); + Vector256 isPositiveZero = Vector256.IsZero(xDouble.AsUInt64()); Vector256 specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector256 isNaNOrNegInf = (Vector256.IsNaN(xDouble) | Vector256.IsNegativeInfinity(xDouble)).AsUInt64(); @@ -155,7 +155,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xFloat).AsUInt32(); + Vector512 isPositiveZero = Vector512.IsZero(xFloat.AsUInt32()); Vector512 specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector512 isNaNOrNegInf = (Vector512.IsNaN(xFloat) | Vector512.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -177,7 +177,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xDouble).AsUInt64(); + Vector512 isPositiveZero = Vector512.IsZero(xDouble.AsUInt64()); Vector512 specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector512 isNaNOrNegInf = (Vector512.IsNaN(xDouble) | Vector512.IsNegativeInfinity(xDouble)).AsUInt64(); From 394d58954957604c6e33fc55820340ad7cdadd03 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:46:45 +0000 Subject: [PATCH 14/14] Remove Half type support due to performance regression Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../netcore/TensorPrimitives.BitDecrement.cs | 21 +++++++------------ .../netcore/TensorPrimitives.BitIncrement.cs | 9 +------- 2 files changed, 8 insertions(+), 22 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 20592bf9902bda..2504c0375592be 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,15 +20,8 @@ public static partial class TensorPrimitives /// /// public static void BitDecrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 - { - if (typeof(T) == typeof(Half) && TryUnaryInvokeHalfAsInt16>(x, destination)) - { - return; - } - + where T : IFloatingPointIeee754 => InvokeSpanIntoSpan>(x, destination); - } /// T.BitDecrement(x) private readonly struct BitDecrementOperator : IUnaryOperator @@ -53,7 +46,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xFloat.AsUInt32()); + Vector128 isPositiveZero = Vector128.IsZero(xFloat).AsUInt32(); Vector128 specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector128 isNaNOrNegInf = (Vector128.IsNaN(xFloat) | Vector128.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -75,7 +68,7 @@ public static Vector128 Invoke(Vector128 x) bits - Vector128.One); // Handle special cases with a single conditional select - Vector128 isPositiveZero = Vector128.IsZero(xDouble.AsUInt64()); + Vector128 isPositiveZero = Vector128.IsZero(xDouble).AsUInt64(); Vector128 specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector128 isNaNOrNegInf = (Vector128.IsNaN(xDouble) | Vector128.IsNegativeInfinity(xDouble)).AsUInt64(); @@ -104,7 +97,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xFloat.AsUInt32()); + Vector256 isPositiveZero = Vector256.IsZero(xFloat).AsUInt32(); Vector256 specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector256 isNaNOrNegInf = (Vector256.IsNaN(xFloat) | Vector256.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -126,7 +119,7 @@ public static Vector256 Invoke(Vector256 x) bits - Vector256.One); // Handle special cases with a single conditional select - Vector256 isPositiveZero = Vector256.IsZero(xDouble.AsUInt64()); + Vector256 isPositiveZero = Vector256.IsZero(xDouble).AsUInt64(); Vector256 specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector256 isNaNOrNegInf = (Vector256.IsNaN(xDouble) | Vector256.IsNegativeInfinity(xDouble)).AsUInt64(); @@ -155,7 +148,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xFloat.AsUInt32()); + Vector512 isPositiveZero = Vector512.IsZero(xFloat).AsUInt32(); Vector512 specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero; Vector512 isNaNOrNegInf = (Vector512.IsNaN(xFloat) | Vector512.IsNegativeInfinity(xFloat)).AsUInt32(); @@ -177,7 +170,7 @@ public static Vector512 Invoke(Vector512 x) bits - Vector512.One); // Handle special cases with a single conditional select - Vector512 isPositiveZero = Vector512.IsZero(xDouble.AsUInt64()); + Vector512 isPositiveZero = Vector512.IsZero(xDouble).AsUInt64(); Vector512 specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero; Vector512 isNaNOrNegInf = (Vector512.IsNaN(xDouble) | Vector512.IsNegativeInfinity(xDouble)).AsUInt64(); 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 41b9966f15ed7c..361af7af06576b 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,15 +20,8 @@ public static partial class TensorPrimitives /// /// public static void BitIncrement(ReadOnlySpan x, Span destination) - where T : IFloatingPointIeee754 - { - if (typeof(T) == typeof(Half) && TryUnaryInvokeHalfAsInt16>(x, destination)) - { - return; - } - + where T : IFloatingPointIeee754 => InvokeSpanIntoSpan>(x, destination); - } /// T.BitIncrement(x) private readonly struct BitIncrementOperator : IUnaryOperator