Step 8: numeric tower — exact/inexact predicates + truncate/remainder/modulo

7 new R7RS primitives on the float-based tower (Number of float unchanged):
- exact? / inexact? — integer detection via Float.is_integer
- exact->inexact / inexact->exact — identity / round-to-integer
- truncate — toward zero (floor for positive, ceil for negative)
- remainder — sign follows dividend (= Float.rem)
- modulo — sign follows divisor

8 new tests (2658/2658 pass). No type system, VM, compiler, or parser changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 22:25:40 +00:00
parent 2f3e727a6f
commit b0a4be0f22
3 changed files with 169 additions and 33 deletions

View File

@@ -150,6 +150,35 @@ let () =
let x = as_number x and lo = as_number lo and hi = as_number hi in
Number (Float.max lo (Float.min hi x))
| _ -> raise (Eval_error "clamp: 3 args"));
register "truncate" (fun args ->
match args with
| [a] -> let n = as_number a in Number (if n >= 0.0 then floor n else ceil n)
| _ -> raise (Eval_error "truncate: 1 arg"));
register "remainder" (fun args ->
match args with
| [a; b] -> Number (Float.rem (as_number a) (as_number b))
| _ -> raise (Eval_error "remainder: 2 args"));
register "modulo" (fun args ->
match args with
| [a; b] ->
let a = as_number a and b = as_number b in
let r = Float.rem a b in
Number (if r = 0.0 || (r > 0.0) = (b > 0.0) then r else r +. b)
| _ -> raise (Eval_error "modulo: 2 args"));
register "exact?" (fun args ->
match args with [Number f] -> Bool (Float.is_integer f) | [_] -> Bool false
| _ -> raise (Eval_error "exact?: 1 arg"));
register "inexact?" (fun args ->
match args with [Number f] -> Bool (not (Float.is_integer f)) | [_] -> Bool false
| _ -> raise (Eval_error "inexact?: 1 arg"));
register "exact->inexact" (fun args ->
match args with [Number n] -> Number n | [a] -> Number (as_number a)
| _ -> raise (Eval_error "exact->inexact: 1 arg"));
register "inexact->exact" (fun args ->
match args with
| [Number n] -> if Float.is_integer n then Number n else Number (Float.round n)
| [a] -> Number (Float.round (as_number a))
| _ -> raise (Eval_error "inexact->exact: 1 arg"));
register "parse-int" (fun args ->
let parse_leading_int s =
let len = String.length s in