js-on-sx: Math.hypot and Math.cbrt honour NaN/Infinity/+-0 edges
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s

This commit is contained in:
2026-05-10 12:32:14 +00:00
parent 1e29bba1be
commit 019a0c6105
2 changed files with 37 additions and 13 deletions

View File

@@ -3987,25 +3987,47 @@
(x)
(let
((n (js-to-number x)))
(if
(< n 0)
(- 0 (pow (- 0 n) (/ 1 3)))
(pow n (/ 1 3))))))
(cond
((js-number-is-nan n) (js-nan-value))
((= n (js-infinity-value)) (js-infinity-value))
((= n (- 0 (js-infinity-value))) n)
((= n 0) n)
((< n 0) (- 0 (pow (- 0 n) (/ 1.0 3.0))))
(else (pow n (/ 1.0 3.0)))))))
(define
js-math-hypot
(fn (&rest args) (sqrt (js-math-hypot-loop args 0))))
(fn
(&rest args)
(let
((status (js-math-hypot-scan args false false 0)))
(cond
((= (first status) "inf") (js-infinity-value))
((= (first status) "nan") (js-nan-value))
(else (sqrt (nth status 1)))))))
(define
js-math-hypot-loop
js-math-hypot-scan
(fn
(args acc)
(if
(empty? args)
acc
(let
((n (js-to-number (first args))))
(js-math-hypot-loop (rest args) (+ acc (* n n)))))))
(args saw-inf? saw-nan? acc)
(cond
((empty? args)
(cond
(saw-inf? (list "inf"))
(saw-nan? (list "nan"))
(else (list "ok" acc))))
(else
(let
((n (js-to-number (first args))))
(cond
((= n (js-infinity-value))
(js-math-hypot-scan (rest args) true saw-nan? acc))
((= n (- 0 (js-infinity-value)))
(js-math-hypot-scan (rest args) true saw-nan? acc))
((js-number-is-nan n)
(js-math-hypot-scan (rest args) saw-inf? true acc))
(else
(js-math-hypot-scan (rest args) saw-inf? saw-nan? (+ acc (* n n))))))))))
(begin
(define js-math-sin (fn (x) (sin (js-to-number x))))

View File

@@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green.
Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta.
- 2026-05-10 — **`Math.hypot` and `Math.cbrt` honour spec edges for NaN, ±Infinity, and ±0.** `Math.hypot(NaN, Infinity)` was returning NaN instead of +Infinity (spec: any ±Infinity arg dominates NaN). Rewrote `js-math-hypot` to scan args once tracking inf/nan flags, return +Infinity if any arg is ±Infinity, else NaN if any was NaN, else `sqrt(sum of squares)`. `Math.cbrt(NaN)` was 0 (because `pow(NaN, 1/3)` produced 0 in our path); also `Math.cbrt(-0)` returned +0 instead of -0. Added explicit short-circuits: NaN→NaN, ±Infinity→arg, ±0→arg, plus changed `(/ 1 3)` (rational) to `(/ 1.0 3.0)` (inexact) to avoid rational fractional-power oddities. Result: built-ins/Math/hypot 9/11 → 10/11. Math/cbrt 3/4 → 4/4. conformance.sh: 148/148.
- 2026-05-10 — **`globalThis.globalThis === globalThis`; `Number.prototype.toFixed` honours digit-range and ≥1e21 fallback.** (1) `globalThis` was bound to `nil` in the global object literal (originally to dodge an inspect-cycle hang) — added `(dict-set! js-global "globalThis" js-global)` after the literal so `globalThis.globalThis === globalThis` per spec. (2) `Number.prototype.toFixed` rewrites: RangeError when fractionDigits is NaN or outside `[0,100]` (was silently producing garbage), and for `|x| >= 1e21` returns `js-number-to-string` (the value's own ToString) per spec step 9. conformance.sh: 148/148.
- 2026-05-10 — **`delete <ident>` returns `false` instead of `true` per non-strict spec.** ES non-strict semantics: `delete x` where `x` is a declared binding (variable / function / parameter) returns `false` and does not unbind. Our transpiler was emitting `true` for any `delete <expr>` whose argument wasn't a member or index access. Now `delete <js-ident>``false`, and `delete <js-paren expr>` recurses on the inner expression so `delete (1+2)` still works. Result: language/expressions/delete 14/30 → 18/30 (+4). conformance.sh: 148/148.