-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix VecXX implementation #1851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix VecXX implementation #1851
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,14 @@ | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Runtime.InteropServices; | ||
| using System.Runtime.InteropServices; | ||
| using OpenCvSharp.Internal.Util; | ||
|
|
||
| #pragma warning disable CA1051 | ||
|
|
||
| namespace OpenCvSharp; | ||
|
|
||
| /// <summary> | ||
| /// 2-Tuple of int (System.Int32) | ||
| /// </summary> | ||
| [Serializable] | ||
| [StructLayout(LayoutKind.Sequential)] | ||
| [SuppressMessage("Design", "CA1051: Do not declare visible instance fields")] | ||
| // ReSharper disable once InconsistentNaming | ||
| public struct Vec2i : IVec<Vec2i, int>, IEquatable<Vec2i> | ||
| { | ||
|
|
@@ -84,6 +83,7 @@ public Vec2i(int item0, int item1) | |
| public static Vec2i operator -(Vec2i a, Vec2i b) => a.Subtract(b); | ||
| public static Vec2i operator *(Vec2i a, double alpha) => a.Multiply(alpha); | ||
| public static Vec2i operator /(Vec2i a, double alpha) => a.Divide(alpha); | ||
| public static Vec2i operator -(Vec2i a) => new(SaturateCast.ToInt32(-(long)a.Item0), SaturateCast.ToInt32(-(long)a.Item1)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, in C#, additions and subtractions on int operands are evaluated in the default unchecked context, causing wraparound on overflow. Specifically, 0 - int.MinValue computes to int.MaxValue due to two's complement wraparound (-(-2147483648) = 2147483647, which fits in int), with no overflow occurring. This wrapped int result is then passed to another method unchanged unless widening (e.g., to long) occurs first. There is no automatic widening of both operands to a larger type like long for binary int operations; binary numeric promotions only apply when mixing certain smaller types or with floating-point, but two ints stay as int. The default is unchecked unless the /checked+ compiler option is set or explicit checked blocks are used. Constant expressions are checked at compile-time, but runtime non-constant int arithmetic defaults to unchecked wraparound. Citations:
🏁 Script executed: # First, check if the file exists and get its basic info
ls -la src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs
wc -l src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.csRepository: shimat/opencvsharp Length of output: 212 🏁 Script executed: # Read the file to see line 86 and surrounding context
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | sed -n '80,95p'Repository: shimat/opencvsharp Length of output: 803 🏁 Script executed: # Find the Add and Subtract methods in the file
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | grep -A 2 "public readonly Vec2i Add\|public readonly Vec2i Subtract"Repository: shimat/opencvsharp Length of output: 419 🏁 Script executed: # Get a broader view to understand the structure
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | head -170Repository: shimat/opencvsharp Length of output: 6027 Match the new unary saturation behavior in the binary Line 86 widens to Widen both operands to Minimal fix public readonly Vec2i Add(Vec2i other) => new(
- SaturateCast.ToInt32(Item0 + other.Item0),
- SaturateCast.ToInt32(Item1 + other.Item1));
+ SaturateCast.ToInt32((long)Item0 + other.Item0),
+ SaturateCast.ToInt32((long)Item1 + other.Item1));
public readonly Vec2i Subtract(Vec2i other) => new(
- SaturateCast.ToInt32(Item0 - other.Item0),
- SaturateCast.ToInt32(Item1 - other.Item1));
+ SaturateCast.ToInt32((long)Item0 - other.Item0),
+ SaturateCast.ToInt32((long)Item1 - other.Item1));🤖 Prompt for AI Agents |
||
| #pragma warning restore 1591 | ||
|
|
||
| /// <summary> | ||
|
|
@@ -120,6 +120,11 @@ public int this[int i] | |
| // ReSharper restore InconsistentNaming | ||
| #pragma warning restore 1591 | ||
|
|
||
| #if !NETSTANDARD2_0 | ||
| /// <summary>Returns a <see cref="Span{T}"/> over the 2 elements of this vector.</summary> | ||
| public Span<int> AsSpan() => MemoryMarshal.CreateSpan(ref Item0, 2); | ||
| #endif | ||
|
|
||
| /// <inheritdoc /> | ||
| public readonly bool Equals(Vec2i other) => | ||
| Item0 == other.Item0 && | ||
|
|
@@ -150,10 +155,10 @@ public readonly override bool Equals(object? obj) | |
| public readonly override int GetHashCode() | ||
| { | ||
| #if NETSTANDARD2_0 | ||
| unchecked | ||
| { | ||
| return (Item0 * 397) ^ Item1; | ||
| } | ||
| unchecked | ||
| { | ||
| return (Item0 * 397) ^ Item1; | ||
| } | ||
| #else | ||
| return HashCode.Combine(Item0, Item1); | ||
| #endif | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
In .NET/C#, doesfloat.NaN.Equals(float.NaN)returntruewhilefloat.NaN == float.NaNreturnsfalse?💡 Result:
Yes, in .NET/C#, float.NaN.Equals(float.NaN) returns true while float.NaN == float.NaN returns false. This behavior follows the IEEE 754 floating-point standard for the == operator, which treats NaN as not equal to anything, including itself. However, the Equals method on float (Single) is overridden to return true for two NaNs to satisfy the Object.Equals contract requirements (reflexivity: x.Equals(x) must be true, and consistency for use in collections like dictionaries). Official Microsoft documentation for Single.NaN explicitly demonstrates this: Console.WriteLine("NaN == NaN: {0}", Single.NaN == Single.NaN); // False Console.WriteLine("NaN.Equals(NaN): {0}", Single.NaN.Equals(Single.NaN)); // True The same applies to double.NaN. Use float.IsNaN to detect NaN values, as direct comparisons always fail.
Citations:
Align
==/!=operators withEqualsmethod to handle NaN consistently.The operators at lines 142–149 use raw
floatcomparisons (==/!=), which follow IEEE 754 semantics whereNaN == NaNisfalse. However,Equals(Vec2f)at line 128 usesSingle.Equals, which returnstruefor two NaN values. This creates an inconsistency:(NaN, 1).Equals((NaN, 1))returnstrue, but(NaN, 1) == (NaN, 1)returnsfalse.Either delegate the operators to
Equals:Or update
Equalsto use raw comparisons. Choose one approach to ensure consistency.🤖 Prompt for AI Agents