Skip to content

Commit 081d220

Browse files
[release/10.0] fix Vector2/3 EqualsAny (#124223)
Backport of #123594 to release/10.0 /cc @tannergooding @kasperk81 ## Customer Impact - [x] Customer reported - [ ] Found internally Reported in #123586, developers using some of the new APIs exposed on Vector2 and Vector3 could get non-deterministic results in some scenarios. ## Regression - [ ] Yes - [x] No These are new APIs. ## Testing Explicit tests covering the scenarios were added in addition to manual verification of the codegen. ## Risk Low. These are net new APIs which could accidentally include invalid elements in the accelerated comparison. The fix was to ensure they used the existing centralized helpers that were exposed to help ensure such code was being consistently handled and avoid such problems. The APIs had simply not gotten checked in using them as intended. --------- Co-authored-by: kasperk81 <83082615+kasperk81@users.noreply.github.com>
1 parent 7101b3d commit 081d220

File tree

4 files changed

+195
-20
lines changed

4 files changed

+195
-20
lines changed

src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,5 +1896,89 @@ public void CreateScalarUnsafeTest()
18961896
vector = Vector2.CreateScalarUnsafe(float.E);
18971897
Assert.Equal(float.E, vector.X);
18981898
}
1899+
1900+
[Fact]
1901+
public void EqualsAnyTest()
1902+
{
1903+
// Test case that would have failed before fix: no elements match, but 3rd/4th elements (0) would match
1904+
Assert.False(Vector2.EqualsAny(Vector2.Create(1, 2), Vector2.Create(3, 4)));
1905+
1906+
// Test cases where elements actually match
1907+
Assert.True(Vector2.EqualsAny(Vector2.Create(1, 2), Vector2.Create(1, 4))); // X matches
1908+
Assert.True(Vector2.EqualsAny(Vector2.Create(1, 2), Vector2.Create(3, 2))); // Y matches
1909+
Assert.True(Vector2.EqualsAny(Vector2.Create(1, 2), Vector2.Create(1, 2))); // All match
1910+
}
1911+
1912+
[Fact]
1913+
public void GreaterThanAllTest()
1914+
{
1915+
// Test case that would have failed before fix: all 2 elements pass, but 3rd/4th (0 > 0) fails
1916+
Assert.True(Vector2.GreaterThanAll(Vector2.Create(2, 3), Vector2.Create(1, 2)));
1917+
1918+
// Test cases where not all elements pass
1919+
Assert.False(Vector2.GreaterThanAll(Vector2.Create(1, 3), Vector2.Create(1, 2))); // X not greater
1920+
Assert.False(Vector2.GreaterThanAll(Vector2.Create(2, 2), Vector2.Create(1, 2))); // Y not greater
1921+
}
1922+
1923+
[Fact]
1924+
public void GreaterThanOrEqualAnyTest()
1925+
{
1926+
// Test case that would have failed before fix: no elements pass, but 3rd/4th (0 >= 0) would pass
1927+
Assert.False(Vector2.GreaterThanOrEqualAny(Vector2.Create(1, 2), Vector2.Create(3, 4)));
1928+
1929+
// Test cases where elements actually pass
1930+
Assert.True(Vector2.GreaterThanOrEqualAny(Vector2.Create(3, 2), Vector2.Create(3, 4))); // X equal
1931+
Assert.True(Vector2.GreaterThanOrEqualAny(Vector2.Create(1, 4), Vector2.Create(3, 4))); // Y equal
1932+
Assert.True(Vector2.GreaterThanOrEqualAny(Vector2.Create(4, 2), Vector2.Create(3, 4))); // X greater
1933+
}
1934+
1935+
[Fact]
1936+
public void LessThanAllTest()
1937+
{
1938+
// Test case that would have failed before fix: all 2 elements pass, but 3rd/4th (0 < 0) fails
1939+
Assert.True(Vector2.LessThanAll(Vector2.Create(1, 2), Vector2.Create(2, 3)));
1940+
1941+
// Test cases where not all elements pass
1942+
Assert.False(Vector2.LessThanAll(Vector2.Create(2, 2), Vector2.Create(2, 3))); // X not less
1943+
Assert.False(Vector2.LessThanAll(Vector2.Create(1, 3), Vector2.Create(2, 3))); // Y not less
1944+
}
1945+
1946+
[Fact]
1947+
public void LessThanOrEqualAnyTest()
1948+
{
1949+
// Test case that would have failed before fix: no elements pass, but 3rd/4th (0 <= 0) would pass
1950+
Assert.False(Vector2.LessThanOrEqualAny(Vector2.Create(3, 4), Vector2.Create(1, 2)));
1951+
1952+
// Test cases where elements actually pass
1953+
Assert.True(Vector2.LessThanOrEqualAny(Vector2.Create(1, 4), Vector2.Create(1, 2))); // X equal
1954+
Assert.True(Vector2.LessThanOrEqualAny(Vector2.Create(3, 2), Vector2.Create(1, 2))); // Y equal
1955+
Assert.True(Vector2.LessThanOrEqualAny(Vector2.Create(0, 4), Vector2.Create(1, 2))); // X less
1956+
}
1957+
1958+
[Fact]
1959+
public void GreaterThanOrEqualAllTest()
1960+
{
1961+
// Test case that would have failed before fix: all 2 elements pass, but 3rd/4th (0 >= 0) could give false positive
1962+
// with undefined upper elements from AsVector128Unsafe
1963+
Assert.True(Vector2.GreaterThanOrEqualAll(Vector2.Create(2, 3), Vector2.Create(1, 2)));
1964+
Assert.True(Vector2.GreaterThanOrEqualAll(Vector2.Create(1, 2), Vector2.Create(1, 2))); // All equal
1965+
1966+
// Test cases where not all elements pass
1967+
Assert.False(Vector2.GreaterThanOrEqualAll(Vector2.Create(0, 3), Vector2.Create(1, 2))); // X not greater or equal
1968+
Assert.False(Vector2.GreaterThanOrEqualAll(Vector2.Create(2, 1), Vector2.Create(1, 2))); // Y not greater or equal
1969+
}
1970+
1971+
[Fact]
1972+
public void LessThanOrEqualAllTest()
1973+
{
1974+
// Test case that would have failed before fix: all 2 elements pass, but 3rd/4th could give false positive
1975+
// with undefined upper elements from AsVector128Unsafe
1976+
Assert.True(Vector2.LessThanOrEqualAll(Vector2.Create(1, 2), Vector2.Create(2, 3)));
1977+
Assert.True(Vector2.LessThanOrEqualAll(Vector2.Create(1, 2), Vector2.Create(1, 2))); // All equal
1978+
1979+
// Test cases where not all elements pass
1980+
Assert.False(Vector2.LessThanOrEqualAll(Vector2.Create(3, 2), Vector2.Create(2, 3))); // X not less or equal
1981+
Assert.False(Vector2.LessThanOrEqualAll(Vector2.Create(1, 4), Vector2.Create(2, 3))); // Y not less or equal
1982+
}
18991983
}
19001984
}

src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,5 +1924,96 @@ public void CreateScalarUnsafeTest()
19241924
vector = Vector3.CreateScalarUnsafe(float.E);
19251925
Assert.Equal(float.E, vector.X);
19261926
}
1927+
1928+
[Fact]
1929+
public void EqualsAnyTest()
1930+
{
1931+
// Test case that would have failed before fix: no elements match, but 4th element (0) would match
1932+
Assert.False(Vector3.EqualsAny(Vector3.Create(1, 2, 3), Vector3.Create(4, 5, 6)));
1933+
1934+
// Test cases where elements actually match
1935+
Assert.True(Vector3.EqualsAny(Vector3.Create(1, 2, 3), Vector3.Create(1, 5, 6))); // X matches
1936+
Assert.True(Vector3.EqualsAny(Vector3.Create(1, 2, 3), Vector3.Create(4, 2, 6))); // Y matches
1937+
Assert.True(Vector3.EqualsAny(Vector3.Create(1, 2, 3), Vector3.Create(4, 5, 3))); // Z matches
1938+
Assert.True(Vector3.EqualsAny(Vector3.Create(1, 2, 3), Vector3.Create(1, 2, 3))); // All match
1939+
}
1940+
1941+
[Fact]
1942+
public void GreaterThanAllTest()
1943+
{
1944+
// Test case that would have failed before fix: all 3 elements pass, but 4th (0 > 0) fails
1945+
Assert.True(Vector3.GreaterThanAll(Vector3.Create(2, 3, 4), Vector3.Create(1, 2, 3)));
1946+
1947+
// Test cases where not all elements pass
1948+
Assert.False(Vector3.GreaterThanAll(Vector3.Create(1, 3, 4), Vector3.Create(1, 2, 3))); // X not greater
1949+
Assert.False(Vector3.GreaterThanAll(Vector3.Create(2, 2, 4), Vector3.Create(1, 2, 3))); // Y not greater
1950+
Assert.False(Vector3.GreaterThanAll(Vector3.Create(2, 3, 3), Vector3.Create(1, 2, 3))); // Z not greater
1951+
}
1952+
1953+
[Fact]
1954+
public void GreaterThanOrEqualAnyTest()
1955+
{
1956+
// Test case that would have failed before fix: no elements pass, but 4th (0 >= 0) would pass
1957+
Assert.False(Vector3.GreaterThanOrEqualAny(Vector3.Create(1, 2, 3), Vector3.Create(4, 5, 6)));
1958+
1959+
// Test cases where elements actually pass
1960+
Assert.True(Vector3.GreaterThanOrEqualAny(Vector3.Create(4, 2, 3), Vector3.Create(4, 5, 6))); // X equal
1961+
Assert.True(Vector3.GreaterThanOrEqualAny(Vector3.Create(1, 5, 3), Vector3.Create(4, 5, 6))); // Y equal
1962+
Assert.True(Vector3.GreaterThanOrEqualAny(Vector3.Create(1, 2, 6), Vector3.Create(4, 5, 6))); // Z equal
1963+
Assert.True(Vector3.GreaterThanOrEqualAny(Vector3.Create(5, 2, 3), Vector3.Create(4, 5, 6))); // X greater
1964+
}
1965+
1966+
[Fact]
1967+
public void LessThanAllTest()
1968+
{
1969+
// Test case that would have failed before fix: all 3 elements pass, but 4th (0 < 0) fails
1970+
Assert.True(Vector3.LessThanAll(Vector3.Create(1, 2, 3), Vector3.Create(2, 3, 4)));
1971+
1972+
// Test cases where not all elements pass
1973+
Assert.False(Vector3.LessThanAll(Vector3.Create(2, 2, 3), Vector3.Create(2, 3, 4))); // X not less
1974+
Assert.False(Vector3.LessThanAll(Vector3.Create(1, 3, 3), Vector3.Create(2, 3, 4))); // Y not less
1975+
Assert.False(Vector3.LessThanAll(Vector3.Create(1, 2, 4), Vector3.Create(2, 3, 4))); // Z not less
1976+
}
1977+
1978+
[Fact]
1979+
public void LessThanOrEqualAnyTest()
1980+
{
1981+
// Test case that would have failed before fix: no elements pass, but 4th (0 <= 0) would pass
1982+
Assert.False(Vector3.LessThanOrEqualAny(Vector3.Create(4, 5, 6), Vector3.Create(1, 2, 3)));
1983+
1984+
// Test cases where elements actually pass
1985+
Assert.True(Vector3.LessThanOrEqualAny(Vector3.Create(1, 5, 6), Vector3.Create(1, 2, 3))); // X equal
1986+
Assert.True(Vector3.LessThanOrEqualAny(Vector3.Create(4, 2, 6), Vector3.Create(1, 2, 3))); // Y equal
1987+
Assert.True(Vector3.LessThanOrEqualAny(Vector3.Create(4, 5, 3), Vector3.Create(1, 2, 3))); // Z equal
1988+
Assert.True(Vector3.LessThanOrEqualAny(Vector3.Create(0, 5, 6), Vector3.Create(1, 2, 3))); // X less
1989+
}
1990+
1991+
[Fact]
1992+
public void GreaterThanOrEqualAllTest()
1993+
{
1994+
// Test case that would have failed before fix: all 3 elements pass, but 4th could give false positive
1995+
// with undefined upper element from AsVector128Unsafe
1996+
Assert.True(Vector3.GreaterThanOrEqualAll(Vector3.Create(2, 3, 4), Vector3.Create(1, 2, 3)));
1997+
Assert.True(Vector3.GreaterThanOrEqualAll(Vector3.Create(1, 2, 3), Vector3.Create(1, 2, 3))); // All equal
1998+
1999+
// Test cases where not all elements pass
2000+
Assert.False(Vector3.GreaterThanOrEqualAll(Vector3.Create(0, 3, 4), Vector3.Create(1, 2, 3))); // X not greater or equal
2001+
Assert.False(Vector3.GreaterThanOrEqualAll(Vector3.Create(2, 1, 4), Vector3.Create(1, 2, 3))); // Y not greater or equal
2002+
Assert.False(Vector3.GreaterThanOrEqualAll(Vector3.Create(2, 3, 2), Vector3.Create(1, 2, 3))); // Z not greater or equal
2003+
}
2004+
2005+
[Fact]
2006+
public void LessThanOrEqualAllTest()
2007+
{
2008+
// Test case that would have failed before fix: all 3 elements pass, but 4th could give false positive
2009+
// with undefined upper element from AsVector128Unsafe
2010+
Assert.True(Vector3.LessThanOrEqualAll(Vector3.Create(1, 2, 3), Vector3.Create(2, 3, 4)));
2011+
Assert.True(Vector3.LessThanOrEqualAll(Vector3.Create(1, 2, 3), Vector3.Create(1, 2, 3))); // All equal
2012+
2013+
// Test cases where not all elements pass
2014+
Assert.False(Vector3.LessThanOrEqualAll(Vector3.Create(3, 2, 3), Vector3.Create(2, 3, 4))); // X not less or equal
2015+
Assert.False(Vector3.LessThanOrEqualAll(Vector3.Create(1, 4, 3), Vector3.Create(2, 3, 4))); // Y not less or equal
2016+
Assert.False(Vector3.LessThanOrEqualAll(Vector3.Create(1, 2, 5), Vector3.Create(2, 3, 4))); // Z not less or equal
2017+
}
19272018
}
19282019
}

src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,12 @@ public static float Cross(Vector2 value1, Vector2 value2)
500500
/// <inheritdoc cref="Vector4.EqualsAll(Vector4, Vector4)" />
501501
[Intrinsic]
502502
[MethodImpl(MethodImplOptions.AggressiveInlining)]
503-
public static bool EqualsAll(Vector2 left, Vector2 right) => Vector128.EqualsAll(left.AsVector128Unsafe(), right.AsVector128Unsafe());
503+
public static bool EqualsAll(Vector2 left, Vector2 right) => AllWhereAllBitsSet(Equals(left, right));
504504

505505
/// <inheritdoc cref="Vector4.EqualsAny(Vector4, Vector4)" />
506506
[Intrinsic]
507507
[MethodImpl(MethodImplOptions.AggressiveInlining)]
508-
public static bool EqualsAny(Vector2 left, Vector2 right) => Vector128.EqualsAny(left.AsVector128Unsafe(), right.AsVector128Unsafe());
508+
public static bool EqualsAny(Vector2 left, Vector2 right) => AnyWhereAllBitsSet(Equals(left, right));
509509

510510
/// <inheritdoc cref="Vector128.MultiplyAddEstimate(Vector128{float}, Vector128{float}, Vector128{float})" />
511511
[Intrinsic]
@@ -520,12 +520,12 @@ public static float Cross(Vector2 value1, Vector2 value2)
520520
/// <inheritdoc cref="Vector4.GreaterThanAll(Vector4, Vector4)" />
521521
[Intrinsic]
522522
[MethodImpl(MethodImplOptions.AggressiveInlining)]
523-
public static bool GreaterThanAll(Vector2 left, Vector2 right) => Vector128.GreaterThanAll(left.AsVector128Unsafe(), right.AsVector128Unsafe());
523+
public static bool GreaterThanAll(Vector2 left, Vector2 right) => AllWhereAllBitsSet(GreaterThan(left, right));
524524

525525
/// <inheritdoc cref="Vector4.GreaterThanAny(Vector4, Vector4)" />
526526
[Intrinsic]
527527
[MethodImpl(MethodImplOptions.AggressiveInlining)]
528-
public static bool GreaterThanAny(Vector2 left, Vector2 right) => Vector128.GreaterThanAny(left.AsVector128Unsafe(), right.AsVector128Unsafe());
528+
public static bool GreaterThanAny(Vector2 left, Vector2 right) => AnyWhereAllBitsSet(GreaterThan(left, right));
529529

530530
/// <inheritdoc cref="Vector4.GreaterThanOrEqual(Vector4, Vector4)" />
531531
[Intrinsic]
@@ -535,12 +535,12 @@ public static float Cross(Vector2 value1, Vector2 value2)
535535
/// <inheritdoc cref="Vector4.GreaterThanOrEqualAll(Vector4, Vector4)" />
536536
[Intrinsic]
537537
[MethodImpl(MethodImplOptions.AggressiveInlining)]
538-
public static bool GreaterThanOrEqualAll(Vector2 left, Vector2 right) => Vector128.GreaterThanOrEqualAll(left.AsVector128Unsafe(), right.AsVector128Unsafe());
538+
public static bool GreaterThanOrEqualAll(Vector2 left, Vector2 right) => AllWhereAllBitsSet(GreaterThanOrEqual(left, right));
539539

540540
/// <inheritdoc cref="Vector4.GreaterThanOrEqualAny(Vector4, Vector4)" />
541541
[Intrinsic]
542542
[MethodImpl(MethodImplOptions.AggressiveInlining)]
543-
public static bool GreaterThanOrEqualAny(Vector2 left, Vector2 right) => Vector128.GreaterThanOrEqualAny(left.AsVector128Unsafe(), right.AsVector128Unsafe());
543+
public static bool GreaterThanOrEqualAny(Vector2 left, Vector2 right) => AnyWhereAllBitsSet(GreaterThanOrEqual(left, right));
544544

545545
/// <inheritdoc cref="Vector4.Hypot(Vector4, Vector4)" />
546546
[Intrinsic]
@@ -650,12 +650,12 @@ public static float Cross(Vector2 value1, Vector2 value2)
650650
/// <inheritdoc cref="Vector4.LessThanAll(Vector4, Vector4)" />
651651
[Intrinsic]
652652
[MethodImpl(MethodImplOptions.AggressiveInlining)]
653-
public static bool LessThanAll(Vector2 left, Vector2 right) => Vector128.LessThanAll(left.AsVector128Unsafe(), right.AsVector128Unsafe());
653+
public static bool LessThanAll(Vector2 left, Vector2 right) => AllWhereAllBitsSet(LessThan(left, right));
654654

655655
/// <inheritdoc cref="Vector4.LessThanAny(Vector4, Vector4)" />
656656
[Intrinsic]
657657
[MethodImpl(MethodImplOptions.AggressiveInlining)]
658-
public static bool LessThanAny(Vector2 left, Vector2 right) => Vector128.LessThanAny(left.AsVector128Unsafe(), right.AsVector128Unsafe());
658+
public static bool LessThanAny(Vector2 left, Vector2 right) => AnyWhereAllBitsSet(LessThan(left, right));
659659

660660
/// <inheritdoc cref="Vector4.LessThanOrEqual(Vector4, Vector4)" />
661661
[Intrinsic]
@@ -665,12 +665,12 @@ public static float Cross(Vector2 value1, Vector2 value2)
665665
/// <inheritdoc cref="Vector4.LessThanOrEqualAll(Vector4, Vector4)" />
666666
[Intrinsic]
667667
[MethodImpl(MethodImplOptions.AggressiveInlining)]
668-
public static bool LessThanOrEqualAll(Vector2 left, Vector2 right) => Vector128.LessThanOrEqualAll(left.AsVector128Unsafe(), right.AsVector128Unsafe());
668+
public static bool LessThanOrEqualAll(Vector2 left, Vector2 right) => AllWhereAllBitsSet(LessThanOrEqual(left, right));
669669

670670
/// <inheritdoc cref="Vector4.LessThanOrEqualAny(Vector4, Vector4)" />
671671
[Intrinsic]
672672
[MethodImpl(MethodImplOptions.AggressiveInlining)]
673-
public static bool LessThanOrEqualAny(Vector2 left, Vector2 right) => Vector128.LessThanOrEqualAny(left.AsVector128Unsafe(), right.AsVector128Unsafe());
673+
public static bool LessThanOrEqualAny(Vector2 left, Vector2 right) => AnyWhereAllBitsSet(LessThanOrEqual(left, right));
674674

675675
/// <inheritdoc cref="Vector4.Load(float*)" />
676676
[Intrinsic]

0 commit comments

Comments
 (0)