CNA-BUG-068: Vector2/3/4 and Matrix division by a scalar divide every component where XNA 4.0 multiplies by one reciprocal

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

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

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.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index