CNA-BUG-074: Vector, Matrix, Quaternion and CurveKey hash codes use raw float bits, so +0.0f and -0.0f components compare equal but hash differently
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
FloatHash returns the IEEE bit pattern, so Equals and GetHashCode disagree for values with a negative-zero component; .NET's Single.GetHashCode returns 0 for both zeros, keeping XNA's hashes consistent with Equals.
- Identifier
CNA-BUG-074- Category
- Bug
- Subsystem
- Math & geometry
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- GetHashCode() of Vector2, Vector3, Vector4, Matrix, Quaternion and CurveKey
Expected behaviour
The Equals/GetHashCode contract: equal values have equal hashes. .NET Framework's Single.GetHashCode returns 0 when the value is 0, so XNA's component-sum hashes agree for +0 and -0.
Actual behaviour at TARGET
Each of Vector3.cpp, Vector2.cpp, Vector4.cpp, Matrix.cpp, Quaternion.cpp and CurveKey.cpp has a local FloatHash that memcpys the float into a uint32_t. Vector3(0,0,0) == Vector3(-0.0f,0,0) is true, yet the hashes differ by 0x80000000.
Source locations
modules/math/src/Vector3.cpp— FloatHash and Vector3::GetHashCodemodules/math/src/Matrix.cpp— FloatHash and Matrix::GetHashCodemodules/math/src/Quaternion.cpp— FloatHash and Quaternion::GetHashCode
Evidence
Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. .NET behaviour from the mscorlib implementation of Single.GetHashCode.
Focused reproduction
// Illustrative; not compiled or run for this entry.
Vector3 a(0.0f, 1.0f, 2.0f), b(-0.0f, 1.0f, 2.0f);
bool eq = a.Equals(b); // true
bool sameHash = a.GetHashCode() == b.GetHashCode(); // false in CNA, true in XNA
Current tests
ObjectEqualityContractTests.cpp exists, but no case uses a negative zero.
Regression test
Add a signed-zero pair to ObjectEqualityContractTests for each type.
Blast radius
Hash containers keyed on vectors or matrices (for example a vertex-welding map) that can receive -0.0f from negation or rounding.
Workaround
Normalise -0.0f to 0.0f before hashing.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Deep dives
- Math value types: equality
- Known issues
- Bug index