CNA-BUG-063: Vector3::Length, LengthSquared, Distance and DistanceSquared accumulate in float, not at the width CNA measured XNA to use

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

✓

Evidence basis: source-verified at the pinned commit; recorded by CNA's own run (not repeated here); 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.

CNA's own measurement of the XNA 4.0 runtime found Vector3.Distance summed at extended precision (a float sum matched none of 200 pairs), and BoundingSphere.cpp follows that rule, but the public Vector3 length and distance functions still sum in float.

Identifier
CNA-BUG-063
Category
Bug
Subsystem
Math & geometry
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Recorded by CNA: CNA's own recorded run, not repeated here
Tests touching this area
Yes: see Current tests
Affected contract
Vector3::Length(), LengthSquared(), Vector3::Distance and Vector3::DistanceSquared (all overloads); the matching C ABI vector routes

Expected behaviour

XNA 4.0 is a 32-bit assembly whose float arithmetic runs on the x87 unit, so x*x + y*y + z*z is accumulated at extended precision and narrowed once. CNA recorded this against the genuine runtime: over 200 random point pairs a float accumulation reproduced XNA's Vector3.Distance on none and a double accumulation on all (plan_xna_sample_xnb_sweep.md, XNASWEEP-134, quoted in the comment on SquaredLength in BoundingSphere.cpp). CNA applies the same widening to Matrix::Multiply, Matrix::Invert and Vector3::Transform.

Actual behaviour at TARGET

In Vector3.cpp, Length() is std::sqrt((X * X) + (Y * Y) + (Z * Z)) and DistanceSquared sums the three squared differences, all in float; Distance is the square root of that. Only the private helpers SquaredLength, WideLength and WideDistance inside BoundingSphere.cpp accumulate in double. By CNA's own recorded measurement (a float accumulation matched XNA's Distance on none of 200 pairs) the public functions therefore differ from XNA in the last bit on those pairs; how often they differ on typical inputs was not measured. Vector2 and Vector4 have the same float shape; they were not measured.

Source locations

Evidence

Checked by reading at 009d40f5; nothing was built or executed for this entry. The float accumulation is read from the function bodies; the XNA behaviour is CNA's recorded measurement, not re-run here, and the public functions' own output was not compared with XNA. Hence the confidence is strong rather than reproduced.

Independent re-verification: Checked by reading at 009d40f5; nothing was built or executed for this entry. The float accumulation is read from the function bodies; the XNA behaviour is CNA's recorded measurement (XNASWEEP-134, quoted in the comment on SquaredLength), not re-run here, and its 200 pairs are not committed as a corpus: the committed framework oracle has multiply, invert, rotation, transform and angle families only. An audit-side Python model of the float and wide accumulation rules reproduces CNA's own committed counts (a float accumulation matches 48 of 168 whole-matrix products and 8 of 60 Vector3 transforms in matrix-oracle.txt, a wide one all of them), yet on individual scalar results the float rule differs from the wide one on only roughly 15 to 30 percent of Distance results and 30 to 45 percent of DistanceSquared results for uniform random points. A claim that the public functions differ from XNA for most inputs is therefore not established. The confidence is recorded-by-cna, not reproduced.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

Vector3Tests.cpp checks length and distance with a tolerance; BoundingSphereOracleTests.cpp compares bits for CreateFromPoints only, and the matrix oracle does not include length or distance.

Regression test

Add Vector3.Length, Distance and DistanceSquared cases to the recorded framework oracle (the 200 measured pairs are the natural set) and compare bits, with a negative control showing that a float accumulation fails, as MatrixOracleTests does.

Blast radius

Every caller, with last-bit effects only: inside math, BoundingSphere::Contains and Intersects (sphere and point), CreateFromBoundingBox, CreateMerged, Plane::Normalize and the frustum's plane normalisation; outside it, callers in audio, content import and the C ABI. Exact-boundary containment answers can flip; nothing else changes.

Workaround

Where bit-exact XNA parity matters, accumulate the squares in double and narrow once, as BoundingSphere.cpp's private helpers do.

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

Known issues
Bug index