From a0e8b64f5cf0bef20a130a6ccbb7a11298b79581 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 02:50:21 +0000 Subject: [PATCH] ocaml: phase 4 integer division semantics + Int module + max_int/min_int (+5 tests, 525 total) Three things in this commit: 1. Integer / is now truncate-toward-zero on ints, IEEE on floats. The eval-op handler for '/' checks (number? + (= (round x) x)) on both sides; if both integral, applies host floor/ceil based on sign; otherwise falls through to host '/'. 2. Fixes Int.rem, which was returning 0 because (a - b * (a / b)) was using float division: 17 - 5 * 3.4 = 0.0. Now Int.rem 17 5 = 2. 3. Int module fleshed out: max_int / min_int / zero / one / minus_one, succ / pred / neg, add / sub / mul / div / rem, equal, compare. Also adds globals: max_int, min_int, abs_float, float_of_int, int_of_float (the latter two are identity in our dynamic runtime). 17 / 5 = 3 -17 / 5 = -3 (trunc toward zero) Int.rem 17 5 = 2 Int.compare 5 3 = 1 --- lib/ocaml/eval.sx | 10 +++++++++- lib/ocaml/runtime.sx | 20 ++++++++++++++++++++ lib/ocaml/test.sh | 19 +++++++++++++++++++ plans/ocaml-on-sx.md | 7 +++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index 0f2ee6e5..f14a8ef3 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -418,7 +418,15 @@ ((= op "+") (+ lhs rhs)) ((= op "-") (- lhs rhs)) ((= op "*") (* lhs rhs)) - ((= op "/") (/ lhs rhs)) + ((= op "/") + ;; OCaml's `/` is integer division on ints, float on floats. + ;; Pick floor on ints. + (cond + ((and (number? lhs) (number? rhs) + (= (round lhs) lhs) (= (round rhs) rhs)) + (let ((q (/ lhs rhs))) + (if (>= q 0) (floor q) (ceil q)))) + (else (/ lhs rhs)))) ((= op "+.") (+ lhs rhs)) ((= op "-.") (- lhs rhs)) ((= op "*.") (* lhs rhs)) diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index b0ec999e..24b86568 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -466,6 +466,21 @@ let abs n = if n < 0 then 0 - n else n let max a b = if a > b then a else b let min a b = if a < b then a else b + let max_int = 4611686018427387903 + let min_int = -4611686018427387904 + let zero = 0 + let one = 1 + let minus_one = -1 + let succ n = n + 1 + let pred n = n - 1 + let neg n = 0 - n + let add a b = a + b + let sub a b = a - b + let mul a b = a * b + let div a b = a / b + let rem a b = a - b * (a / b) + let equal a b = a = b + let compare a b = if a < b then -1 else if a > b then 1 else 0 end ;; module Float = struct @@ -832,6 +847,11 @@ let string_of_float f = _string_of_float f let string_of_bool b = if b then \"true\" else \"false\" let int_of_string s = _int_of_string s + let max_int = 4611686018427387903 + let min_int = -4611686018427387904 + let abs_float f = if f < 0.0 then 0.0 -. f else f + let float_of_int n = n + let int_of_float f = f ") (define ocaml-stdlib-loaded false) diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 4cf26cf0..fe118bc5 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1298,6 +1298,18 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 5044) (eval "(ocaml-run \"let a = Array.of_list [1;2;3;4;5] in Array.mem 3 a\")") +;; ── Integer division + Int module + max_int ────────────────── +(epoch 5050) +(eval "(ocaml-run \"17 / 5\")") +(epoch 5051) +(eval "(ocaml-run \"-17 / 5\")") +(epoch 5052) +(eval "(ocaml-run \"Int.rem 17 5\")") +(epoch 5053) +(eval "(ocaml-run \"Int.compare 5 3\")") +(epoch 5054) +(eval "(ocaml-run \"max_int + min_int\")") + EPOCHS OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -2062,6 +2074,13 @@ check 5042 "Array.append 6-len sum" '21' check 5043 "Array.exists = 6" 'true' check 5044 "Array.mem 3 [1..5]" 'true' +# ── Int module + integer division ─────────────────────────────── +check 5050 "17 / 5 = 3 (int div)" '3' +check 5051 "-17 / 5 = -3 (trunc-zero)" '-3' +check 5052 "Int.rem 17 5" '2' +check 5053 "Int.compare 5 3" '1' +check 5054 "max_int + min_int (host int)" '0' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tests passed" diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index 90a1154f..455ef84c 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,13 @@ _Newest first._ binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree * 'a tree`) with insert + in-order traversal. Tests parametric ADT, recursive match, List.append, List.fold_left. +- 2026-05-09 Phase 4 — integer `/` is now truncate-toward-zero on + ints, IEEE on floats. Both operands integral → host floor/ceil based + on sign; otherwise host `/`. Fixes `Int.rem` (which was returning 0 + for `Int.rem 17 5` because `a / b` was producing a float). Also adds + Int.{max_int,min_int,zero,one,minus_one,succ,pred,neg,add,sub,mul, + div,rem,equal,compare} and global max_int/min_int/abs_float/ + float_of_int/int_of_float (+5 tests, 525 total). - 2026-05-09 Phase 6 — Array.sort/stable_sort/fast_sort + sub + append + exists + for_all + mem (+5 tests, 520 total). All delegate to the corresponding List operation on the cell's