The unrolled sorting network for loat and double uses the > operator for compare-and-swap:
\\csharp
if (e0 > e1) { float temp = e0; e0 = e1; e1 = temp; }
\\
In C#, any comparison involving NaN returns alse (e.g. NaN > x is alse, x > NaN is alse). This means NaN values are never swapped and remain in their original positions.
Array.Sort and Span.Sort use IComparable.CompareTo, which defines NaN as less than all other values (including -∞). So the generated Sort produces different output than the BCL sort for inputs containing NaN.
Root cause
ScalarEmitter.EmitSortMethod emits the same > comparison for all types, including loat/double. It should either emit a NaN-aware comparison for floating-point types (matching Single.CompareTo/Double.CompareTo ordering), or fall back to the comparer-based path.
The SIMD paths (SimdX86Emitter, SimdArmEmitter) likely have the same issue since hardware min/max instructions also treat NaN specially.
Affected files
- SortingNetworks.Generators/ScalarEmitter.cs (line ~37) — source generator emits �{a} > e{b} for all types
- SortingNetworks.Generators/SimdX86Emitter.cs — SIMD sorting for x86
- SortingNetworks.Generators/SimdArmEmitter.cs — SIMD sorting for ARM
Found via Copilot code review on #8.
The unrolled sorting network for loat and double uses the > operator for compare-and-swap:
\\csharp
if (e0 > e1) { float temp = e0; e0 = e1; e1 = temp; }
\\
In C#, any comparison involving NaN returns alse (e.g. NaN > x is alse, x > NaN is alse). This means NaN values are never swapped and remain in their original positions.
Array.Sort and Span.Sort use IComparable.CompareTo, which defines NaN as less than all other values (including -∞). So the generated Sort produces different output than the BCL sort for inputs containing NaN.
Root cause
ScalarEmitter.EmitSortMethod emits the same > comparison for all types, including loat/double. It should either emit a NaN-aware comparison for floating-point types (matching Single.CompareTo/Double.CompareTo ordering), or fall back to the comparer-based path.
The SIMD paths (SimdX86Emitter, SimdArmEmitter) likely have the same issue since hardware min/max instructions also treat NaN specially.
Affected files
Found via Copilot code review on #8.