Skip to content

fix(libc): preserve modf decomposition and floating-point status - #11139

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
JJenkx:fix/libc-modf-floating-environment
Oct 27, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
JJenkx:fix/libc-modf-floating-environment

Conversation

@JJenkx

@JJenkx JJenkx commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

modf and modff use host std::modf, which changes subnormal results/flags under DAZ/FTZ and quiets signaling NaNs. For example, the supplied libraries return the least positive subnormal unchanged with a zero integral part and no new flags, while the host wrapper raises SSE denormal-operand status or returns zero under DAZ.

Split finite values using their exponent/fraction bits, subtracting the integral part only when the input has a fractional part and magnitude at least one. Preserve signed zeros and NaN payload/quiet bits. The measured signaling-NaN behavior differs by width: double preserves flags; float preserves the signaling bit but raises SSE invalid. The float path uses a quiet SSE comparison to reproduce that side effect without changing the returned/stored value. errno and rounding/control state are preserved.

The contract comes from the bundled libc.prx of PPSA03766 and PPSA07923, not a console measurement. The converted entry offsets are respectively modf 0x516b0 / 0x4cfe0 and modff 0x51850 / 0x4d180. The implementation is derived from the observed contract; no module code or disassembly is included. Remaining integration/platform gaps and unmasked exceptions are recorded in debt; the isolated Windows coverage added after publication is described below. No new exports or title/FPS benefit are claimed.

Tested

Latest conflict refresh (2026-10-26): exact head c48ed84d47746c3586e7ff04551628b405ec7492 on main bf77a9e9cba4b02c358779b35295176dd9cae164. The conflict was in adjacent debt notes; the production implementations and regression checks are preserved. Main's unrelated _LCosh/_LSinh debt is retained.

Rebuilt libc and the focused test executables on Linux. guest_math and guest_math_exact pass: two groups × three repetitions (6 passes), zero skips. Conventions, whitespace and final main merge checks pass; the source tree is clean. This is a mechanical refresh with a debt-note resolution, so no new console, Windows, title, GPU or negative-control run is claimed. Earlier measurements retain their original exact pins.

Patch/preservation checks, exact pins, build/test logs, JUnit and push receipts. SHA-256: 196fbf4f07b96515f278558ca31d139759c24195adfd35ddebc93f03c186c92b; downloaded copy verified.

Linux, GCC16.2.1, Release. Head 0504933bcc840b8acc8a2d5e139bec4f3d54e734, based on main ae7d59258ea77b090c6be392e2eb2872a9744d00. Fresh libc, guest_math_tests and guest_math_exact_tests builds pass.

Patch, numeric reference matrices, probes and build/test evidence — SHA256 02ae64f5c2a0fa0a90978865fbab94abaf437034474c3e099c1f0978dbb41f9f. Archive downloaded and hash-verified. Original modules, private disassembly and game assets are excluded. The author did not run Windows. Physical-console, native-title, full Windows shared-library integration and performance tests remain unverified.

Independent Windows replay of this exact head by thepoppfish: Windows 11 build 26200, GCC 16.2.0-6 UCRT, complete actual Math.cpp, FloatingPoint.cpp and MathExact.cpp linked into a native probe. Against the archived Tetris/PPSA07923 rows, the candidate has 0 differences in 786,864 double calls and 270,720 float calls, while original host wrappers differ in 852 and 684 rows respectively. The external replay checks both results, errno, raw SSE/x87 flags/control and normalized C exception flags, with explicit x87 control seeding. The unchanged 3,072-call committed fixture also passes in an external focused CTest executable. This is the reviewer's isolated Windows execution, not a new console/module measurement or a full Windows shared-lib/NID-patch, all-guest-math, game or performance qualification. The reviewed source remains unchanged at 0504933bcc8.

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 (Codex)

Contributor-reported Windows follow-up: @ycbuzi tested exact head c48ed84d47 merged onto main 32b9227eec, Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 UCRT r7, Release. guest_math and guest_math_exact each passed three times; restoring only main's production Math.cpp made guest_math abort while guest_math_exact passed. This is the contributor's report, without independently inspected raw logs or an assertion stack, and retains those exact pins. Original report.

@MarcZX10 MarcZX10 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.

Reviewed the exact head 0504933bcc840b8acc8a2d5e139bec4f3d54e734. The exponent/mantissa masks cover subnormal, fractional, integral, infinity and NaN cases without converting the input through a host floating-point decomposition; the float signaling-NaN comparison is kept separate so it can set the observed invalid flag while preserving the returned bits. The debt entry accurately records that native console execution and unmasked exceptions remain unverified.

Local validation:

  • g++ -std=c++20 -Icore/libs -Icore/libs/prx/libc/include -fsyntax-only on Math.cpp and GuestMath.cpp: passed.
  • python3 tools/check_conventions.py --base upstream/main --head HEAD: passed.
  • An ephemeral result-bit comparison against host std::modf over 500,000 random finite bit patterns per width passed. This checks decomposition only; it does not validate the console-specific exception behavior.

The author reports focused tests and sampled differential comparisons against both supplied libc modules, including floating-point flags and controls. Hosted eligibility, conventions, intake, labels and report pass; product/library jobs are skipped. The hosted relinker jobs are unrelated to this libc change and remained pending at review time. I found no blocking issue in this PR-specific diff.

@thepoppfish thepoppfish 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.

Reviewed exact head 0504933bcc840b8acc8a2d5e139bec4f3d54e734 over ae7d59258ea77b090c6be392e2eb2872a9744d00. Approve the focused modf/modff change. I added Windows native result/status coverage to the existing source review and the author's Linux measurements; no new finding.

Windows 11 build 26200, GCC 16.2.0-6 UCRT, C++20 -O1, CMake 4.3.1/Ninja 1.13.2:

Function Inputs × control states Current head mismatches Original host-wrapper mismatches
modf 16,393 × 48 = 786,864 0 852
modff 5,640 × 48 = 270,720 0 684

These are 2,115,168 actual native function calls across both versions, compared with the Tetris/PPSA07923 rows in the author's published reference archive. I downloaded the 25,446,826-byte asset and verified SHA256 02ae64f5c2a0fa0a90978865fbab94abaf437034474c3e099c1f0978dbb41f9f against both the release asset metadata and PR description. Its recorded source and fixture hashes match the actual Git blobs at this head. All listed inputs occur exactly once in each of the 48 combinations: four rounding modes × four DAZ/FTZ states × three sticky-flag seeds; the coverage check rejects duplicates and missing combinations.

The native probe links the complete actual Math.cpp, FloatingPoint.cpp and MathExact.cpp for each version, with an explicit __error adapter returning &errno as Process.cpp does. An unused NotImplemented hook throws if reached. The tested math functions and floating-point environment operations are not replaced. Comparisons check fraction/integral bits, errno, raw SSE flags/control and x87 flags/control, plus C exception flags normalized by their named masks. The broad probe explicitly seeds the recorded raw x87 control word, avoiding an accidental comparison of Windows and Linux default precision settings.

Separately, I extracted the unchanged newly committed ModfCase, CheckModfCases, CheckModfDecomposition and x87 read helpers into an external focused executable: all 3,072 checks pass, one local CTest in 0.06 s. This preserves the committed tests' setup and expectations; it is not a run of the rest of guest_math. Conventions against main 6bc113969895900890ae9bb4c473beb840ba12db, whitespace and clean-checkout checks pass.

The bit split avoids arithmetic for tiny/integral inputs and retains NaN bits. The float-only quiet comparison supplies the recorded signaling-NaN SSE invalid side effect without changing returned/stored bits. Both original-wrapper controls fail the reference comparison, so this exercises a behavior difference rather than merely accepting both implementations.

Scope: the Windows candidate executions are mine; the original converted-module measurements are the author's archived Linux data. I did not load either original module locally, rerun FAR, run a PS5, execute a game, test unmasked traps, build/NID-patch the full shared libc, run all guest_math tests or verify hosted CI. This adds isolated Windows coverage; full Windows guest/module integration and the remaining debt are still unverified. No exhaustive or performance claim.

AI-assisted review and native replay (OpenAI Codex).

@github-actions

github-actions Bot commented Oct 25, 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 docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6085 core/libs/prx/libc/src/Math.cpp
PR 6295 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 6713 docs/dev/debt/libc.md
PR 7081 docs/dev/debt/libc.md
PR 7386 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8417 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9929 docs/dev/debt/libc.md
PR 10324 docs/dev/debt/libc.md
PR 10433 docs/dev/debt/libc.md
PR 10749 docs/dev/debt/libc.md
PR 10989 docs/dev/debt/libc.md
PR 11109 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
docs/dev/debt/libc.md
PR 11332 docs/dev/debt/libc.md
PR 11341 docs/dev/debt/libc.md
PR 11632 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
docs/dev/debt/libc.md
PR 11634 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
docs/dev/debt/libc.md

@ycbuzi

ycbuzi commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

A Windows run of this head, since Windows isn't in the description: Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release. 0504933bcc merged onto main 898a7fef4f with no conflicts.

  • guest_math_tests, guest_math_exact_tests and guest_dinkumware_internals_tests build with no warnings or errors.
  • guest_math, guest_math_exact and guest_dinkumware_internals pass both runs of ctest --repeat until-fail:2.
  • Negative control: libc/src/Math.cpp taken from main, with the tests unchanged. guest_math then aborts (0xc0000409; its Require prints nothing), and the other two still pass.

This PR and #10345 both change Math.cpp and GuestMath.cpp. Those merge cleanly with each other, but docs/dev/debt/libc.md conflicts.

AI-assisted: yes (Claude Code)

@wachi35676

Copy link
Copy Markdown
Contributor

Windows run of head 0504933bc merged onto main 0f34ebb85, WinLibs GCC 15.2.0, Release. This is the in-tree shared-library build and ctest that the description lists as unverified on Windows.

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

@oneandonlydean oneandonlydean self-assigned this Oct 26, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@JJenkx
JJenkx force-pushed the fix/libc-modf-floating-environment branch from 0504933 to ebaff78 Compare October 26, 2026 12:27
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 26, 2026
@JJenkx
JJenkx force-pushed the fix/libc-modf-floating-environment branch from ebaff78 to c48ed84 Compare October 26, 2026 13:35
@JJenkx

JJenkx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Latest conflict refresh (2026-10-26): exact head c48ed84d47746c3586e7ff04551628b405ec7492 on main bf77a9e9cba4b02c358779b35295176dd9cae164. The conflict was in adjacent debt notes; the production implementations and regression checks are preserved. Main's unrelated _LCosh/_LSinh debt is retained.

Rebuilt libc and the focused test executables on Linux. guest_math and guest_math_exact pass: two groups × three repetitions (6 passes), zero skips. Conventions, whitespace and final main merge checks pass; the source tree is clean. This is a mechanical refresh with a debt-note resolution, so no new console, Windows, title, GPU or negative-control run is claimed. Earlier measurements retain their original exact pins.

Patch/preservation checks, exact pins, build/test logs, JUnit and push receipts. SHA-256: 196fbf4f07b96515f278558ca31d139759c24195adfd35ddebc93f03c186c92b; downloaded copy verified.

AI-assisted: yes.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@ycbuzi

ycbuzi commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

@JJenkx A native Windows run of c48ed84d47: Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, merged onto main 32b9227eec with no conflicts. Plain ctest --repeat until-fail:3.

  • guest_math and guest_math_exact pass, 3 of 3. The build has no warnings.
  • Control with main's libc/src/Math.cpp and this PR's test: guest_math aborts (0xc0000409), and guest_math_exact still passes.

AI-assisted: yes (Claude Code)

@JJenkx

JJenkx commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Recorded the additional result in the PR description with its exact tested pins and limits.

Contributor-reported Windows follow-up: @ycbuzi tested exact head c48ed84d47 merged onto main 32b9227eec, Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 UCRT r7, Release. guest_math and guest_math_exact each passed three times; restoring only main's production Math.cpp made guest_math abort while guest_math_exact passed. This is the contributor's report, without independently inspected raw logs or an assertion stack, and retains those exact pins. Original report.

AI-assisted: yes.

@oneandonlydean
oneandonlydean merged commit d5f7d86 into boykopovar:main Oct 27, 2026
21 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.

6 participants