Skip to content

feature: fmod floor-mod builtin + document mod signedness#482

Merged
danieljohnmorris merged 7 commits into
mainfrom
feature/fmod-builtin
May 20, 2026
Merged

feature: fmod floor-mod builtin + document mod signedness#482
danieljohnmorris merged 7 commits into
mainfrom
feature/fmod-builtin

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

@danieljohnmorris danieljohnmorris commented May 20, 2026

Summary

  • Adds fmod a b: floor-mod that is always non-negative when b > 0, equivalent to Python a % b
  • Documents mod's C-style signed-remainder semantics in SPEC.md, ai.txt, and ilo-builtins-math.md
  • Eliminates the (raw + 7) % 7 workaround every TZ/weekday persona needed

Fixes pending.md P1 #9.

Repro before/after

Before: fmod did not exist, mod -1 7 returns -1, forcing agents to write (raw + 7) % 7.

After:

mod -1 7    -> -1  (unchanged, C-style)
fmod -1 7   -> 6   (floor-mod, Python/JS semantics)
fmod -7 7   -> 0
fmod 8 7    -> 1
fmod -3 -7  -> -3  (negative divisor: sign matches divisor)
fmod 3 -7   -> -4

What's in the diff

  • add fmod builtin: Builtin::Fmod enum variant, OP_FMOD opcode (187), dispatcher in tree-walker interpreter, VM, JIT (jit_fmod helper), and AOT (cranelift compile path)
  • add tests and example: cross-engine regression tests for positive/negative/fractional/zero/negative-divisor inputs, error cases; examples/fmod.ilo with weekday and ring-position demos
  • doc sync: SPEC.md fmod row + mod signedness note, ai.txt fmod entry, skills/ilo/ilo-builtins-math.md callout, CHANGELOG.md unreleased entry
  • fmt: cargo fmt clean-up
  • test: negative divisor: pin fmod 3 -7 = -4, fmod -3 -7 = -3, fmod -10 -7 = -3 cross-engine

Notes

  • Builtin::ALL insertion at position 9: Inserting Fmod between Mod and Clamp reorders the on-wire tag for every later variant. Pre-1.0 this is acceptable (no persisted bytecode contract yet), but flagging for the changelog when 1.0 ships.
  • SKILL.md not updated: intentional. SKILL.md is the top-level skill index and does not enumerate builtins; math-builtin documentation lives in skills/ilo/ilo-builtins-math.md which is updated in this PR.

Test plan

  • fmod -1 7 = 6 on --vm, --jit, AOT
  • fmod -7 7 = 0 on --vm, --jit
  • fmod 1.5 1 = 0.5 on --vm, --jit
  • fmod -1.5 1 = 0.5 on --vm, --jit
  • fmod -3 -7 = -3 on --vm, --jit (negative divisor)
  • fmod 3 -7 = -4 on --vm, --jit (negative divisor)
  • fmod -10 -7 = -3 on --vm, --jit (negative divisor)
  • fmod 5 0 errors on --vm, --jit
  • fmod "a" 7 errors on --vm, --jit
  • mod -1 7 still returns -1 (no regression)
  • cargo fmt -- --check clean
  • Full test suite passes (CI will confirm)

Follow-ups

  • None required; mod stays as-is (breaking change would be 26.X)

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 68.08511% with 30 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/interpreter/mod.rs 11.76% 15 Missing ⚠️
src/vm/compile_cranelift.rs 14.28% 12 Missing ⚠️
src/vm/mod.rs 92.68% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris force-pushed the feature/fmod-builtin branch 2 times, most recently from 98ecd05 to e03b0c0 Compare May 20, 2026 14:32
fmod a b returns a non-negative result when b > 0, equivalent to Python
a % b. Eliminates the (raw + 7) % 7 workaround every TZ/weekday persona
needed with signed mod.

Implemented as OP_FMOD (opcode 187), jit_fmod helper, and tree-walker
dispatch. mod is unchanged.
Cross-engine regression tests for positive/negative/fractional inputs,
zero dividend, zero divisor error, and non-number error. Example file
shows weekday and ring-position usage patterns.
SPEC.md: fmod row + mod now notes C-style signed semantics.
ai.txt: fmod entry next to mod with floor-mod semantics note.
ilo-builtins-math.md: mod gotcha callout, fmod listed.
CHANGELOG.md: unreleased entry for fmod.
Result sign matches divisor (Python semantics): fmod 3 -7 = -4,
fmod -3 -7 = -3, fmod -10 -7 = -3. Pinned across VM and JIT to
prevent silent drift if the sign-correction branch is touched.
Error messages now say "fmod: modulo by zero" across tree/VM/JIT so
agents reading stderr can tell which builtin tripped without re-reading
the source. Non-number tests now assert "fmod" or "number" appears in
the message, and cover both arg positions.

NaN/Inf inputs propagate via IEEE 754 f64 % semantics. This matches the
existing policy for every other math builtin (abs, sqrt, pow, /) and the
"NaN propagates; comparisons false" note in ilo-builtins-math.md.
Diverging fmod from that policy would create cross-builtin inconsistency
and surprise agents trained on numpy/Python where NaN-propagate is the
norm. Pinned with regression tests:

  fmod NaN 7   -> NaN  (propagates)
  fmod 5 Inf   -> 5    (% Inf returns dividend)

SPEC.md and ai.txt now call out the NaN/Inf policy on the fmod row.
@danieljohnmorris danieljohnmorris merged commit 8a6a533 into main May 20, 2026
4 of 5 checks passed
@danieljohnmorris danieljohnmorris deleted the feature/fmod-builtin branch May 20, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant