Skip to content

fix(libc): honour the rounding mode in rint and rintf - #8211

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
ycbuzi:pr/libc-rint-rounding-mode
Oct 20, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
ycbuzi:pr/libc-rint-rounding-mode

Conversation

@ycbuzi

@ycbuzi ycbuzi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What

rint and rintf give wrong results under a directed rounding mode. MathExact.cpp is built without -frounding-math, so GCC expands std::rint as an add and subtract of 2^52 (2^23 for rintf) on the magnitude and puts the sign back afterwards, which is exact only when rounding to nearest: under FE_DOWNWARD, rint(-1.5) returns -1 where C and FreeBSD's rint(3) give -2. lrint and llrint were already right, because they call the host libm's llrint, which honours the mode.

MathExact.cpp now builds with -frounding-math. GuestMathExact checks rint, rintf, rintl, lrint and llrint of -1.5 and 1.5 in all four rounding modes, and that the mode is unchanged afterwards.

The exports landed in #6345 without the flag. #2235 (closed without merge) had specified -frounding-math for this translation unit, and its rounding test first caught the failure (Привалов Александр Владимирович).

Tested

Windows 11, MinGW-w64 GCC 15.2.0 (x86_64-ucrt-posix-seh r7), Release, on main 784f82b plus this commit:

  • guest_math_exact, guest_math, guest_floating_point, guest_float_builtins and guest_scan_count pass.
  • With main's libc/CMakeLists.txt (no flag) and the new checks kept, guest_math_exact fails (exit 0xc0000409); with the flag it passes.
  • gcc -O3 -S of __builtin_rint with this compiler: without the flag, the add and subtract of 2^52 (0x4330000000000000) is applied to |x| and the sign is restored afterwards; with it, copysign(2^52, x) is added to and subtracted from x itself.

Linux (GCC, Release): the new checks fail without the flag and pass with it; the same five tests pass.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

🤖 Generated with Claude Code

MathExact.cpp was built without -frounding-math, so GCC compiled
std::rint as an add and subtract of 2^52 on the magnitude, which is exact
only when rounding to nearest: under FE_DOWNWARD rint(-1.5) returned -1
and rintf(-1.5f) -1, where C and FreeBSD's libm give -2. lrint and llrint
were right: they call the host libm's llrint, which honours the mode.

MathExact.cpp now builds with -frounding-math. GuestMathExact checks
rint, rintf, rintl, lrint and llrint of -1.5 and 1.5 in all four rounding
modes and that the mode is unchanged.

The failure was first caught by the rounding test of
boykopovar#2235 (Привалов Александр Владимирович).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 5021 core/libs/prx/libc/CMakeLists.txt
PR 6044 core/libs/prx/libc/CMakeLists.txt
PR 6819 core/libs/prx/libc/CMakeLists.txt
PR 7081 core/libs/prx/libc/CMakeLists.txt
PR 7201 core/libs/prx/libc/CMakeLists.txt
PR 7332 core/libs/prx/libc/CMakeLists.txt
core/libs/tests/GuestMathExact.cpp
PR 7715 core/libs/prx/libc/CMakeLists.txt

@oneandonlydean oneandonlydean self-assigned this Oct 20, 2026
ycbuzi added a commit to ycbuzi/AnyPS5 that referenced this pull request Oct 20, 2026
@wachi35676

Copy link
Copy Markdown
Contributor

Windows run of 5c1eba11a merged onto main 02fb1ce53 (clean). Windows 11 (10.0.26300), Ryzen 5 9600X, WinLibs GCC 15.2.0 UCRT, Release, BUILD_TESTING=ON.

  • guest_math_exact and guest_math: pass 3 of 3. With main's libc/CMakeLists.txt (no -frounding-math) and the new checks kept, guest_math_exact fails (exit 0xc0000409).
  • Wider check. I compared the exports with FreeBSD 11.4's lib/msun/src/s_rint.c and s_rintf.c, compiled into a probe with -O0 -frounding-math, bit for bit and on the raised flags (inexact, invalid, overflow, underflow, divide-by-zero). The probe ran 824,599 doubles in each of the four rounding modes: n, n+0.25, n+0.5 (and the doubles on either side of it) and n+0.75 for n in [-2000, 2000]; 2^k plus or minus {0, 0.5, 1, 1.5} for k = 20..60; ±0; ±0.3; ±0.49999999999999994; denormals; ±DBL_MAX; ±inf; quiet and signalling NaN; 400k random bit patterns; and 400k uniform values in ±1e7. rintf got the same inputs converted to float.
libc build mode rint value mismatches rintf value mismatches flag mismatches
main nearest / toward zero 0 / 0 0 / 0 0
main downward 415,790 324,360 0
main upward 316,060 139,092 0
this PR all four 0 0 0

For example, on main downward gives rint(-1999.25) = -1999 (FreeBSD: -2000), and upward gives rintf(-1998.5) = -1999 (FreeBSD: -1998). With the PR, every result matches FreeBSD bit for bit, including the sign of a zero result (rint(-0.3) upward is -0.0) and NaN. Inexact is raised exactly when FreeBSD raises it.

  • rintl matches x87 frndint (FreeBSD's amd64 s_rintl.S) on every value in all four modes, on both builds.

A side finding that this PR does not change: for a finite double outside the int64 range, lrint, llrint and lrintf return 0 on both builds (187,579 such inputs here). FreeBSD 11.4 amd64 s_lrint.S/s_llrint.S is a bare cvtsd2si, which returns INT64_MIN (0x8000000000000000). It would be a separate fix.

AI-assisted: yes (Claude Code); results above were run on this machine.

@oneandonlydean
oneandonlydean merged commit d1f8ae2 into boykopovar:main Oct 20, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants