Let Compare and Equals treat float noise as equal numbers
game-maker / gate (push) Successful in 3m4s
game-maker / suite (push) Failing after 9m47s

A graph that checks "time - late == 6" after On timer (#224) failed,
because the difference comes out as 5.999999999999972. Both values are
sums of measured frame times, and they round along different paths.
The only workaround was a Round node in front of every comparison.

- New helper nearlyEqual in values.ts. Two numbers count as equal when
  |a - b| <= 1e-9 * max(1, |a|, |b|). The tolerance grows with the
  size of the values, so it holds for hours of summed dt.
- Compare uses the rule for all six operators. "<" and ">" only hold
  when the values are not nearly equal, "<=" and ">=" also hold when
  they are. So "==" and ">" are never true at the same time.
- Equals uses the same rule when both inputs are real numbers. Objects
  still compare by identity, everything else as text. The node
  description says so.
- The tolerance is fixed, there is no parameter on the node. Physics
  values come from float32 in Rapier and are not covered on purpose.
- The log display is unchanged and still prints the raw number.

Tests:
- ./check.sh: 156 files, 2311 tests, CHECK EXIT 0 (before 2300).
- 11 new cases in node-catalog.test.ts, including the value from the
  log against 6, growth with magnitude, 6.001 != 6 and 1e-6 > 0.
- Counter-check exact compare: exactly the 8 noise cases fail.
- Counter-check "<"/">" without the exclusion: exactly 2 cases fail.
- No e2e change, the check sum stays at 1374.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-24 08:30:45 +02:00
1 parent 6fecb2e841
commit 9d6a0aebd3
4 files changed
+85 -9

No files matched your search

+61
View File
@@ -685,11 +685,72 @@ const CASES: Record<string, Case[]> = {
{ name: 'equal text', inputs: { a: 'a', b: 'a' }, outputs: { result: true } },
{ name: 'a number equals its text form', inputs: { a: 3, b: '3' }, outputs: { result: true } },
{ name: 'objects compare by identity, not by name', inputs: eqNamesakes(), outputs: { result: false } },
{
name: 'two numbers ignore float noise',
inputs: { a: 5.999999999999972, b: 6 },
outputs: { result: true },
},
{ name: 'two numbers still tell 6.001 from 6', inputs: { a: 6.001, b: 6 }, outputs: { result: false } },
],
Compare: [
{ name: 'less than', inputs: { a: 1, b: 2 }, params: { op: '<' }, outputs: { result: true } },
{ name: 'greater or equal', inputs: { a: 2, b: 2 }, params: { op: '>=' }, outputs: { result: true } },
{ name: 'not equal', inputs: { a: 2, b: 2 }, params: { op: '!=' }, outputs: { result: false } },
// time - late after 6 s of summed dt (#224): the noise counts as equal under every operator.
{
name: '== ignores float noise',
inputs: { a: 5.999999999999972, b: 6 },
params: { op: '==' },
outputs: { result: true },
},
{
name: '!= ignores float noise',
inputs: { a: 5.999999999999972, b: 6 },
params: { op: '!=' },
outputs: { result: false },
},
{
name: '< ignores float noise',
inputs: { a: 5.999999999999972, b: 6 },
params: { op: '<' },
outputs: { result: false },
},
{
name: '>= ignores float noise',
inputs: { a: 5.999999999999972, b: 6 },
params: { op: '>=' },
outputs: { result: true },
},
{
name: '> ignores float noise',
inputs: { a: 6.000000000000028, b: 6 },
params: { op: '>' },
outputs: { result: false },
},
{
name: '<= ignores float noise',
inputs: { a: 6.000000000000028, b: 6 },
params: { op: '<=' },
outputs: { result: true },
},
{
name: 'the tolerance grows with the values',
inputs: { a: 1e6, b: 1e6 + 1e-4 },
params: { op: '==' },
outputs: { result: true },
},
{
name: 'a real difference stays one',
inputs: { a: 6.001, b: 6 },
params: { op: '==' },
outputs: { result: false },
},
{
name: 'a tiny value still differs from 0',
inputs: { a: 1e-6, b: 0 },
params: { op: '>' },
outputs: { result: true },
},
{
name: 'no operator chosen falls back to the default "<"',
inputs: { a: 1, b: 2 },