Menu ▾ ▴

#5354 bfloat of a float at fpprec below 16 truncates instead of rounding, and differently for negative values

open
nobody
38 minutes ago
38 minutes ago
No

This bug was discovered by Claude Opus 5.5 and reproduced on Maxima Git commit c62924c (2026-09-26) with SBCL 2.2.9. It supersedes [#4853], which reported the same bug. Related: [#4738].

Where: src/float.lisp, FLOATTOFP

When fpprec has fewer bits than the float, the mantissa is shifted with ASH, which floors: positive floats are truncated toward zero and negative ones rounded away from zero, giving up to 1 ulp error and bfloat(-x) # -bfloat(x). The fix rounds with FPROUND and takes the shift from *M; for fpprec >= 16 the result is unchanged.

Steps

  1. Verify that the existing behavior is incorrect: run the reproducer below; it gives the result under "Before the fix". Judge for yourself that this is wrong instead of relying on the ticket: where the bug allows it, try some related inputs of your own, or check the result another way, for example numerically or against the documentation.
  2. Edit the file src/float.lisp as described under "Fix".
  3. Rebuild Maxima.
  4. Verify that the behavior is now correct: the reproducer gives the result under "Correct result". Judge for yourself that this is right, not only that it matches: where the bug allows it, try some related inputs of your own, including some that the fix should leave unchanged.
  5. Add the test case(s) under "Test cases" to tests/ulp_tests.mac, at the end of the file.
  6. Add the line * \#5354 bfloat of a float at fpprec below 16 truncates instead of rounding, and differently for negative values to ChangeLog, under "Bug fixes for numbered bugs:".
  7. Commit the fix; a proposed Git commit message is under "Commit message".
  8. Push the commit. Then set this ticket's status to "closed" and add a comment naming the commit, for example 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.

Reproducer

block([fpprec:5], bfloat(0.7) + bfloat(-0.7));

Before the fix:

-1.9073b-6

Correct result:

0.0b0

Fix

In src/float.lisp (FLOATTOFP, snippet "(let ((scale (- fpprec (integer-length frac))))"), replace

        ;; Scale frac to the desired number of bits, and adjust the
        ;; exponent accordingly.
        (let ((scale (- fpprec (integer-length frac))))
          (list (ash (* sign frac) scale)
                (+ fpprec (- exp scale)))))))

with

        ;; Round frac to the desired number of bits, and adjust the
        ;; exponent by the shift FPROUND reports in *M.
        (let ((man (fpround (* sign frac))))
          (list man (+ fpprec exp *m))))))

The source file indents with tabs (8 columns); the code above shows them as spaces.

Test cases

Add to tests/ulp_tests.mac:

/* FLOATTOFP: at fpprec below 16, bfloat of a float rounds to nearest,
   the same for both signs */
block([fpprec: 5],
  [is(bfloat(-0.7) = -bfloat(0.7)), is(bfloat(0.7) = bfloat(7/10))]);
[true, true]$

Commit message

bfloat(): round a float to nearest at low fpprec

When fpprec has fewer bits than the float, FLOATTOFP shifted the
mantissa with ASH, which floors: positive floats were truncated and
negative ones rounded away from zero, so bfloat(-0.7) was not
-bfloat(0.7) at fpprec 5. The mantissa is now rounded with FPROUND and
the exponent adjusted by the shift it leaves in *M; for fpprec >= 16
nothing changes.

This fixes bug #5354.

Found and fixed by Claude Opus 5.5.

Related

Bugs: #4738
Bugs: #4853

Discussion


Log in to post a comment.