CNA-BUG-068: Vector2/3/4 and Matrix division by a scalar divide every component where XNA 4.0 multiplies by one reciprocal
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.
Divide(v, s), operator/(v, s) and Matrix::Divide(m, s) divide per component where XNA multiplies by 1/s, so results can differ in the last bit; Vector3::operator/= alone uses the reciprocal.
- Identifier
CNA-BUG-068- 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
- Vector2/Vector3/Vector4::Divide(value, float), operator/(value, float), operator/=(float); Matrix::Divide(const Matrix&, float, Matrix&)
Expected behaviour
XNA 4.0 (IL of Vector2/3.Divide(value, float), Vector3/4.op_Division(value, float) and Matrix.Divide(matrix, float)): one div computing 1f / divider, then one mul per component.
Actual behaviour at TARGET
Vector2.cpp, Vector3.cpp, Vector4.cpp and Matrix.cpp divide each component by the scalar (Vector2's operator/= too). Vector3::operator/=(float) alone multiplies by 1.0f / divider, with a comment claiming this matches Vector3::Divide's reciprocal form, which it does not. So v / s, v /= s and XNA's v / s can round differently.
Source locations
modules/math/src/Vector3.cpp— Vector3::Divide(value, float) and operator/=(float) with its commentmodules/math/src/Vector2.cpp— Vector2::Divide(value, float) and operator/=(float)modules/math/src/Vector4.cpp— Vector4::Divide(value, float)modules/math/src/Matrix.cpp— Matrix::Divide(matrix, float, result)
Evidence
Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. XNA arithmetic from the genuine XNA 4.0 IL. The difference is at most a few ULPs per component; the x87 evaluation precision of the 32-bit XNA runtime was not modelled.
Independent re-verification: Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. XNA arithmetic from the genuine XNA 4.0 IL. The difference is at most a few ULPs per component; the x87 evaluation precision of the 32-bit XNA runtime was not modelled. Tests: the scalar-divide cases in the vector and matrix suites mostly divide by 2, whose reciprocal is exact; the few with an inexact reciprocal (Vector2 divided by 3.0f and /= 2.5f, Vector3 /= 3.0f) use dividends that round identically both ways or a 4-ULP tolerance. In particular Vector3CompoundAssignmentTests.cpp::ScalarDivideMatchesTheBinaryOperatorBitForBit divides by 3.0f, and its comment says the scalar divide multiplies by a reciprocal 'as Vector3::Divide does', which is false at TARGET; it passes only because its dividends (1.5, -2, 3.25) round the same both ways (checked with a float32 model), so it pins nothing. The audit model found reciprocal-multiply and division differing on about 14 to 55 percent of random dividends for divisors such as 3, 7 and 0.1. No oracle covers scalar division.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
The vector and matrix tests use values whose reciprocal is exact; no oracle covers scalar division.
Regression test
A table of divisors whose reciprocal is inexact (3, 7, 0.1f) comparing CNA's results with value * (1.0f / s) bit for bit, for every overload including operator/=.
Blast radius
Code that compares results bit-exactly with XNA, or relies on v / s == v * (1 / s); ordinary game math is unaffected in practice.
Workaround
Multiply by a precomputed reciprocal in game code.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Math internals: arithmetic width
- Deep dives
- Vector and matrix numerics: precision
- Known issues
- Bug index