Skip to content

fix(libc): keep floor and floorf at +0 for small positive values when rounding down - #12313

Open
JJenkx wants to merge 2 commits into
boykopovar:mainfrom
JJenkx:windows/libc-floor-rounding-math
Open

JJenkx wants to merge 2 commits into
boykopovar:mainfrom
JJenkx:windows/libc-floor-rounding-math

Conversation

@JJenkx

@JJenkx JJenkx commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

With FE_DOWNWARD, floor and floorf returned -0 for +0, 0.1, 0.5 and positive subnormals. The correct result, and what titles' libc.prx returns, is +0.

Math.cpp is built without -frounding-math. Without it, GCC inlines std::floor as an add-and-subtract-2^52 sequence that is only exact when rounding to nearest. Rounding down, x - x-style steps produce -0. MathExact.cpp is already built with -frounding-math (core/libs/prx/libc/CMakeLists.txt) for this reason, and its rint/rintf match the titles in every mode. floor and floorf move there, next to them, unchanged. I didn't add -frounding-math to all of Math.cpp, since that would change code generation for every other port in it.

GuestMathExact.cpp's CheckRoundingModes now checks, in each of the four rounding modes:

  • floor and floorf of 0.0, 0.1, 0.5 and 0x1.8p-1030 are +0;
  • floor(-0.5) is -1;
  • floor(-0.0) and floorf(-0.0f) keep their signs;
  • floorf of representable positive float subnormals (denorm_min() and 0x1p-130f) and FLT_MIN is +0.

Tested

Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON, on main cc672fdfc6.

  • guest_math_exact and guest_math pass in each of 3 runs.

  • Negative control: with main's Math.cpp and MathExact.cpp and this test, guest_math_exact fails.

  • The cause alone: the same std::floor built with -O2 returns -0 for 0.5, +0, 0.1 and 0x1.8p-1030 under FE_DOWNWARD. With -O2 -frounding-math it returns +0.

  • Against titles' own libc.prx: I loaded PPSA07923's and PPSA05916's bundled libc.prx natively and called floor, floorf, ceil, ceilf, trunc, truncf, round, roundf, rint, rintf, nearbyint and nearbyintf by NID, side by side with this build. The inputs were 27 values (halves, ±0, ±2^52 ± 0.5, 2^53, subnormals, ±1e300, ±inf, NaN) in all four rounding modes, comparing result bits and raised FE_INEXACT/FE_INVALID. Both titles gave the same results:

    • floor, floorf: 5 wrong results each on main (all FE_DOWNWARD), 0 with this change;
    • ceil, ceilf, trunc, truncf, rint, rintf: 0 before and after.

    The remaining differences are flags only and outside this PR:

Linux follow-up: guest_math_exact and guest_math pass three runs each at exact head 6829885e23; environment and results. macOS remains untested.

Current Linux qualification — 2026-10-29

Refreshed against main 5454cd2e455994d992aca39335ce60507eeda1eb.

Linux qualification at b6fc3a313a9c7e7a48ace600d0a7fff7beb1b4b6 (Arch Linux 7.2.9, Ryzen 9 7950X, GCC 16.2.1, Ninja Release, BUILD_TESTING=ON): guest_math, guest_math_exact, guest_dinkumware_internals each pass three consecutive runs (9/9, no skips). The public_headers audit passes at this exact head. All tracked public-header files match the pinned main; the affected test targets rebuild against those headers. Conventions and whitespace checks pass. The same focused groups passed three times on the pinned main.

Evidence: immutable Linux qualification bundle. Its SHA-256 is 53b095da2f67f55b8aa56ba273681e0330fb39739be844458038dde410fb8cc4. No fresh Windows runtime execution is claimed; older measurements retain their recorded source pins.

AI-assisted: yes (Codex).

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 (Claude Code)

🤖 Generated with Claude Code

@MarcZX10

Copy link
Copy Markdown
Contributor

The implementation change is narrowly scoped and the Windows negative-control/title comparisons are useful. One small test-coverage gap: 0x1.8p-1030 is far below the smallest positive float, so converting it to float for floorf yields zero; those floorf assertions do not exercise a small positive nonzero float. Could you add a representable positive float subnormal (for example std::numeric_limits<float>::denorm_min()) and check floorf(-0.0f) preserves the negative-zero sign? This is a test-strengthening suggestion; it does not change the observed double-path evidence. I’ll wait for the hosted relinker matrix and Windows job before deciding on a review state.

@github-actions

github-actions Bot commented Oct 28, 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 6295 core/libs/prx/libc/src/Math.cpp

@oneandonlydean oneandonlydean self-assigned this Oct 28, 2026
@github-actions github-actions Bot added the libc label Oct 28, 2026
@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Good catch, thanks: 0x1.8p-1030 does convert to a float zero.

6829885e23 adds the float subnormal checks in CheckRoundingModes, in all four modes. floorf of std::numeric_limits<float>::denorm_min(), 0x1p-130f and FLT_MIN must be +0, and floorf(-0.0f) must keep its sign, alongside the existing floor(-0.0) check.

guest_math_exact passes in each of 3 runs, and with main's Math.cpp/MathExact.cpp it fails (Windows 11 Enterprise 10.0.22621, WinLibs GCC 15.2.0 UCRT r7, Release).

AI-assisted: yes (Claude Code).

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Linux run at this PR's exact head 6829885e23 (6829885e23b5d9e5e6bcbee24a7c7baeba46099b), not merged onto anything else; its base on main is cc672fdfc6.

Arch Linux, kernel 7.2.9, Ryzen 9 7950X, g++ (GCC) 16.2.1 20260810, glibc 2.44, x86-64. Release, BUILD_TESTING=ON, Ninja -j4, private worktree.

  • ctest -R '^guest_(math|math_exact)$': passes in each of 3 runs (6 passes).

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean The current macOS relinker job for this head builds successfully, then reports 377/378 CTests passing. Its only failure is the shared Python performance_compare test timing out at 20.28 seconds under parallel CTest; the job gives no assertion failure to diagnose. Failed job and log.

This PR changes only the libc rounding implementation and GuestMathExact.cpp, not that Python test. The timeout alone does not establish a flaky runner or a code regression. Could a maintainer rerun the failed macOS job to distinguish them? I tried the job-rerun endpoint, which returned HTTP 403, “Must have admin rights to Repository.” The exact-head Linux math/exact checks already posted above remain separate evidence; no macOS pass is claimed.

AI-assisted: yes.

@BunnyFl BunnyFl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving head 6829885e23. I read the whole diff and ran firmware 13.42's floor and floorf: the decrypted system libSceLibcInternal.sprx's exports, natively on a PC (not a console), in each of the four rounding modes. The module's rint(±0.5) changes with the mode in the same run, so the mode reaches it. Three identical runs.

  • In every mode, floor of 0, 0.1, 0.5 and 0x1.8p-1030, and floorf of 0, 0.1, 0.5, denorm_min, 0x1p-130f and FLT_MIN, return +0.
  • floor(-0.5) and floorf(-0.5f) return -1; -0 keeps its sign; ±1.5 give 1 and -2.

These are the values the new CheckRoundingModes rows require, so the system module agrees with them. The one failed CI job is the macOS clang++ relinker run, where only performance_compare (tools/tests/test_performance_compare.py, 20 s timeout) timed out; this PR doesn't touch it.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
JJenkx and others added 2 commits October 29, 2026 08:47
… rounding down

Math.cpp is built without -frounding-math, so GCC inlines std::floor
as an add-and-subtract-2^52 sequence that is only exact when rounding
to nearest. With FE_DOWNWARD it returned -0 for +0, 0.1, 0.5 and
positive subnormals, where titles' libc.prx returns +0. Define floor
and floorf in MathExact.cpp, which is built with -frounding-math for
this reason, next to rint and rintf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… mode

The floorf checks converted the double inputs to float, which made
0x1.8p-1030 a zero. Check the float denormal minimum, 0x1p-130f and
FLT_MIN directly, and that floorf(-0.0f) keeps its sign.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JJenkx
JJenkx force-pushed the windows/libc-floor-rounding-math branch from 6829885 to b6fc3a3 Compare October 29, 2026 13:12
@JJenkx

JJenkx commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed against main 5454cd2e455994d992aca39335ce60507eeda1eb.

Linux qualification at b6fc3a313a9c7e7a48ace600d0a7fff7beb1b4b6 (Arch Linux 7.2.9, Ryzen 9 7950X, GCC 16.2.1, Ninja Release, BUILD_TESTING=ON): guest_math, guest_math_exact, guest_dinkumware_internals each pass three consecutive runs (9/9, no skips). The public_headers audit passes at this exact head. All tracked public-header files match the pinned main; the affected test targets rebuild against those headers. Conventions and whitespace checks pass. The same focused groups passed three times on the pinned main.

Evidence: immutable Linux qualification bundle. Its SHA-256 is 53b095da2f67f55b8aa56ba273681e0330fb39739be844458038dde410fb8cc4. No fresh Windows runtime execution is claimed; older measurements retain their recorded source pins.

AI-assisted: yes.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@bunbeans2-arch

Copy link
Copy Markdown
Contributor

A Windows run with GCC 16: Windows 11 Pro 10.0.22631, i9-9900K, MSYS2 UCRT64 GCC 16.1.0, Release, main 673fa86 with this PR's diff (b6fc3a313a) applied.

  • With this PR: guest_math_exact passed 5 of 5.
  • Same tree with only Math.cpp and MathExact.cpp put back to main (this PR's test kept): it failed 5 of 5 at GuestMathExact.cpp:138: requirement failed, which is one of the new floor sign checks in CheckRoundingModes.

AI-assisted: yes (Claude Code)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants