Repository navigation
Description
Summary
A float constant in a pattern's value position, written or bound with $slot, matches every stored float within an absolute f64::EPSILON (about 2.2e-16), not only an equal one. The same comparison written as a predicate is exact, so two ways of asking the same question return different rows.
(transact [[:e1 :m/x 0.00000000000000001]]) ; 1e-17
(query [:find ?e :where [?e :m/x 0.0]]) ; => [[:e1]] wrong
(query [:find ?e :where [?e :m/x ?v] [(= ?v 0.0)]]) ; => [] right
PreparedQuery with $v bound to BindValue::Val(Value::Float(0.0)) also returns :e1. Checked on main (v2.0.9) before and after a checkpoint.
Cause
(f - ff).abs() < f64::EPSILON where a constant float is compared with a stored one:
main:src/query/datalog/matcher.rs:217v3:src/query/datalog/matcher.rs:241,src/query/engine/plan.rs:76(documented as "floats withinf64::EPSILON"),src/query/engine/scan.rs:107and:379
An absolute epsilon does nothing useful at either end of the range. Above 1.0 it is smaller than the gap between adjacent floats, so the comparison is exact. Below that, values that differ by orders of magnitude compare equal: every float in (-2.2e-16, 2.2e-16) matches 0.0, and 1e-20 matches 5e-20. Joins on a variable (JoinKey, IEEE ==), predicates, and the indexes (encode_value / keys::float_bits, exact bytes) are all exact, so this one comparison disagrees with the rest of the engine.
Impact
Wrong rows from a pattern with a float constant when the stored floats include values smaller than about 2.2e-16 in magnitude: probabilities, scientific measurements, embedding components. Nothing is lost or changed on disk. Present on both branches; the v3 engine copied the executor's comparison so that the plan-independence oracle would agree.
Proposed fix
Compare with IEEE ==, as joins and = already do (-0.0 equals 0.0; NaN equals nothing, and NaN can't be stored, #688). Change every site above together so the engine and the executor still agree, and add a regression test for both pattern forms (literal and bound) and the predicate form. v3 first, then a backport to main, following the v2.x data-integrity policy.
🤖 Generated with Claude Code
Activity
docs: sync CHANGELOG, TEST_COVERAGE and CLAUDE.md for #706
test: float constants in rule calls; file tests off wasm32 (#706)
test: float constant in a rule call; file test off wasm32 (#706)
Merge pull request #710 from project-minigraf/fix/706-float-exact-v3
fix(query): float constants match equal floats only (#706) — backport (…
release: v2.0.10
adityamukho commentedon Oct 23, 2026
Fixed in v2.0.10, released 2026-10-23 (crates.io, CHANGELOG). Also fixed on
v3for v3.0.0. Moved to "Fixed in v2.0.10" in #421.