This bug was discovered by Claude Opus 5.5 and reproduced on Maxima Git commit 0d1e40c (2026-09-27) with SBCL 2.2.9. It supersedes [#2197], which reported one of its symptoms.
Where: src/rat3e.lisp, CDISREP
CDISREP turns a rational form back into an expression. When the denominator is a float, it returns the numerator alone, as if that float were always 1.0, and for a float numerator over an integer denominator it builds ((rat) 1.0 2), which is not a valid rational number. With keepfloat: true, ratsimp(1.0*%i/(1.0+1.0*%i)^2) reduces to the rational form with numerator 1.0 and denominator 2.0 and returns 1.0, and ratsimp(1.0*%i/(1+%i)^2) reduces to 1.0 over 2, whose simplification signals a Lisp error. rectform is affected through ABSARG1, which calls SRATSIMP with keepfloat bound to true: rectform(((1.0*%i)/(1.0*%i+1.0)^2)^(1/4)) returns 1.0 instead of 2^(-1/4) = 0.8409. [#2197], where expand of the rational form -8.0/5 signals a Lisp error, is the same bug. The fix divides by a float denominator and returns a float for a float numerator.
src/rat3e.lisp as described under "Fix".tests/rtest9.mac, at the end of the file.make check, or run_testsuite(share_tests=true); in ./maxima-local), and check that the change breaks no test. Failures that also occur without the change (a run on the unchanged build shows which) are pre-existing and can be ignored.* \#5581 with keepfloat:true, ratsimp(1.0*%i/(1.0+1.0*%i)^2) returns 1.0 instead of 0.5 to ChangeLog, under "Bug fixes for numbered bugs:".Fixed by commit [1a2b3c]., with the first six characters of the commit's SHA in the brackets (git rev-parse HEAD | cut -c1-6 prints them). SourceForge links this to the commit.keepfloat: true$ ratsimp(1.0*%i/(1.0+1.0*%i)^2); ratsimp(1.0*%i/(1+%i)^2); rectform(((1.0*%i)/(1.0*%i+1.0)^2)^(1/4));
Before the fix:
1.0
Maxima encountered a Lisp error:
The value 1.0 is not of type INTEGER
1.0
Correct result:
0.5, 0.5, 0.8408964152537145
In src/rat3e.lisp (CDISREP, "((or (equal 1 (cdr x)) (floatp (cdr x)))"), replace
((or (equal 1 (cdr x)) (floatp (cdr x))) (pdisrep (car x)))
with
((or (equal 1 (cdr x)) (and (floatp (cdr x)) (= (cdr x) 1)))
(pdisrep (car x)))
((floatp (cdr x)) (pdisrep (pctimes (/ (cdr x)) (car x))))
In src/rat3e.lisp (CDISREP, "(list '(rat) (* sign n) d))"), replace
(cond ((and (numberp n) (numberp d))
(list '(rat) (* sign n) d))
with
(cond ((and (numberp n) (numberp d))
(if (floatp n)
(/ (* sign n) d)
(list '(rat) (* sign n) d)))
The source file indents with tabs (8 columns); the code above shows them as spaces.
Add these lines to tests/rtest9.mac, separated from the tests before them by two empty lines:
/* Bug #5581: "with keepfloat:true, ratsimp(1.0*%i/(1.0+1.0*%i)^2) returns 1.0 instead of 0.5" */
block ([keepfloat : true],
[ratsimp (1.0*%i/(1.0 + 1.0*%i)^2), ratsimp (%i/(1.0 + %i)^2),
ratsimp (1.0*%i/(1 + %i)^2)]);
[0.5, 0.5, 0.5]$
rectform (((1.0*%i)/(1.0*%i + 1.0)^2)^(1/4));
0.8408964152537145$
block ([keepfloat : true], expand (rat (1.2)*rat (1/2) - 11/5, 0, 0));
-1.6$
rat3e.lisp: fix CDISREP for float numbers
CDISREP returned the numerator alone when the denominator was a float,
as if it were 1.0, and built the invalid number ((rat) 1.0 2) for a
float numerator over an integer. With keepfloat: true,
ratsimp(1.0*%i/(1.0+1.0*%i)^2) returned 1.0 instead of 0.5, and
ratsimp(1.0*%i/(1+%i)^2) signalled a Lisp error. rectform(), which
simplifies with keepfloat bound to true, returned 1.0 for
((1.0*%i)/(1.0*%i+1.0)^2)^(1/4). CDISREP now divides by a float
denominator and returns a float for a float numerator.
This fixes bugs #5581 and #2197.
Found and fixed by Claude Opus 5.5.
Diff:
Fixed by commit [56b6dc].
Related
Commit: [56b6dc]