From 991ef661e3475816a68e8bcdeed36c7c35606a23 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sun, 14 Jun 2026 14:23:54 -0400 Subject: [PATCH] =?UTF-8?q?c=20tier:=20rationals=20on=20int/int=20division?= =?UTF-8?q?=20=E2=80=94=20matches=20python=20lumbda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit num_div for two integers used to fall back to double when the quotient wasn't exact. R7RS / python lumbda require exact-in → exact-out for /. Fixed: the rational_normalize path was already wired for the is_exact branch; the int/int branch now calls it too instead of make_double. (/ 67 7) → 67/7 (was 9.5714285714285712) (/ 1 3) → 1/3 (was 0.33333…) (/ 6 2) → 3 (exact stays integer) (+ 1/3 1/6) → 1/2 (rational arithmetic propagates) C native + C-WASM tier now match python lumbda on / between integers. asm tier rationals remain pending — that needs bignums in asm first. native c-test: 205/205 still passes. --- c/types.c | 16 +++++++++------- www/playground/c/lumbda-c.wasm | Bin 288669 -> 288304 bytes www/repl/c/lumbda-c.wasm | Bin 288669 -> 288304 bytes 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/c/types.c b/c/types.c index d99415c..0e044c8 100644 --- a/c/types.c +++ b/c/types.c @@ -419,18 +419,20 @@ Value num_mul(Value a, Value b) { } Value num_div(Value a, Value b) { - /* Integer / integer that divides cleanly stays integer (matches Python / - * Scheme semantics for `/` between exacts when the quotient is exact). - * For bignum case we treat as exact division (truncating to integer when - * quotient is exact, else fall back to double for now — secp256k1 ops - * never use fractional bignum). */ + /* Integer / integer that divides cleanly stays integer; otherwise we + * promote to an exact rational (matches Python lumbda and R7RS: + * `/` between exacts produces an exact result). The previous code + * fell back to double for non-divisible int/int — that broke parity + * with the Python tier on (/ 67 7), (/ 1 3), etc. */ if (IS_INTEGER(a) && IS_INTEGER(b)) { if (big_is_zero(b)) lisp_error("division by zero"); Value q = big_quotient(a, b); Value r = big_remainder(a, b); if (big_is_zero(r)) return q; - /* Inexact fallback. */ - return make_double(as_number_double(a) / as_number_double(b)); + int64_t an, ad, bn, bd; + to_rational(a, &an, &ad); + to_rational(b, &bn, &bd); + return rational_normalize(an * bd, ad * bn); } if (is_exact(a) && is_exact(b)) { int64_t an, ad, bn, bd; diff --git a/www/playground/c/lumbda-c.wasm b/www/playground/c/lumbda-c.wasm index e3aa27705952c69a429c5871dbe32b39f3695c4c..c0835e8d6068b7eee679d396f3d220d237ff2c0e 100755 GIT binary patch delta 645 zcmaKp%WD)t5XSrK8Dsa3y~#pkF+r^!1j&OOC4%T=@Z`mH#fy0HCV0x-oehSNKcP-J zsmH)VkepULi{M>QBI@`6#TSdH*|B!AixAO3>ACe-t&wW^3PFUUXKXxzgd`cO+a9C_^;9*TipY{`OG*eNq37%Wi{HvJN5K2}?Z5d2e84YjDEawHjtiXq#4+yX zsBz~T=J`jCDIDY^)F=4#8$7SZ?z|eF>oWb4)*cmpkmC$reJNVY%ez?MM{n`5O72eA b8~LB{XzA$`Z}xFyz1xRez1{5xJ2!p<#;UIh delta 846 zcmb7C&ubGw6rMMmAKRUsHUaBO(}Xum@JCxky=X7mE>eOQlX&x%n+FdO^wuWPAiXLg z4@5!iKM)b=sZ#HPe}P4z)>uV^nkY7Hd})(-X(CHBz=<6yB(Ul+I(yb;O8jWp8 zEm0uEn$oD9kKeMB1UXB^kB~ZvZV#>17C9|OP&nI$+^d65aB<#;dkAyJk3Yj8TTzjd6Bb<(u*9i0Cbf9$}naA0A{u;Ub&CM4%)wt%-> KoY}J0um1&IVA^c} diff --git a/www/repl/c/lumbda-c.wasm b/www/repl/c/lumbda-c.wasm index e3aa27705952c69a429c5871dbe32b39f3695c4c..c0835e8d6068b7eee679d396f3d220d237ff2c0e 100755 GIT binary patch delta 645 zcmaKp%WD)t5XSrK8Dsa3y~#pkF+r^!1j&OOC4%T=@Z`mH#fy0HCV0x-oehSNKcP-J zsmH)VkepULi{M>QBI@`6#TSdH*|B!AixAO3>ACe-t&wW^3PFUUXKXxzgd`cO+a9C_^;9*TipY{`OG*eNq37%Wi{HvJN5K2}?Z5d2e84YjDEawHjtiXq#4+yX zsBz~T=J`jCDIDY^)F=4#8$7SZ?z|eF>oWb4)*cmpkmC$reJNVY%ez?MM{n`5O72eA b8~LB{XzA$`Z}xFyz1xRez1{5xJ2!p<#;UIh delta 846 zcmb7C&ubGw6rMMmAKRUsHUaBO(}Xum@JCxky=X7mE>eOQlX&x%n+FdO^wuWPAiXLg z4@5!iKM)b=sZ#HPe}P4z)>uV^nkY7Hd})(-X(CHBz=<6yB(Ul+I(yb;O8jWp8 zEm0uEn$oD9kKeMB1UXB^kB~ZvZV#>17C9|OP&nI$+^d65aB<#;dkAyJk3Yj8TTzjd6Bb<(u*9i0Cbf9$}naA0A{u;Ub&CM4%)wt%-> KoY}J0um1&IVA^c}