Skip to content

normalize_to_site_local's f64 intermediate is unobservable: rewriting it to f32 arithmetic leaves 11/11 green #2950

Description

@louistrue

Found while reviewing #2949 (which pins the ty/tz conjuncts of the same function). Deferred rather than folded in, because closing it needs a new fixture rather than another tuple.

The claim nothing tests

rust/ffi/src/lib.rs:154 states the contract in a comment:

Subtract the site translation with f64 precision, then store as f32.

chunk[0] = (chunk[0] as f64 - site_tx) as f32;

Rewrite all three lines to do the arithmetic in f32 instead:

chunk[0] = chunk[0] - site_tx as f32;

11/11 green. The f64 intermediate the comment exists to explain is currently unobservable.

Why it survives

Every coordinate in the fixtures is exactly representable in f32. mesh_a is [0,0,0, 1,2,3], and the translations are 1.0, 123456.0, 1000.5, 999.5, 1000.0. For values like these, (a as f64 - b) as f32 and a - (b as f32) agree bit for bit. There is no input in the suite where the two paths can disagree, so no assertion can separate them.

This is the same shape as the gap #2949 closes, one level down. There the guard had three conjuncts and only one was pinned. Here the precision of the subtraction is asserted in prose and by nothing else.

Why it is worth pinning

The f64 intermediate is not decoration. It is the reason this function can subtract a georeferenced translation without losing the low-order bits of the result, and georeferenced coordinate collapse is a failure mode this repo has hit before (docs/architecture/coordinate-handling.md, and the f32-collapse history in the geometry pipeline).

An unpinned precision contract degrades quietly. Someone simplifying the cast chain, or a lint suggesting the "redundant" as f64, would leave every test green and every large-coordinate model subtly wrong.

What closing it needs

A fixture whose f64 and f32 subtractions genuinely differ: a vertex and a site translation whose difference is not exactly representable at f32 precision, so the two orderings produce different bits. Something in the 100 km range with a fractional vertex offset, chosen so (v as f64 - t) as f32 != v - (t as f32).

Then assert the exact expected f32 bits, not an epsilon comparison. An epsilon wide enough to be comfortable is an epsilon wide enough to admit the bug.

Scope note

Test-only. lib.rs does not need to change; the production code is already correct. This is about making it stay correct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions