Skip to content

feat(libc): implement lgammaf, tgamma, and tgammaf - #6158

Closed
freshstart2066-create wants to merge 1 commit into
boykopovar:mainfrom
freshstart2066-create:feat/libc-gamma
Closed

freshstart2066-create wants to merge 1 commit into
boykopovar:mainfrom
freshstart2066-create:feat/libc-gamma

Conversation

@freshstart2066-create

Copy link
Copy Markdown

What

Guest game engines, procedural generation systems, and audio synthesis runtimes require the true gamma function (tgamma, tgammaf) and single-precision log-gamma (lgammaf). While double-precision lgamma was previously supported, missing tgamma, tgammaf, and lgammaf exports cause dynamic linker resolution failures at guest boot.

This PR implements:

  • lgammaf (lgammaf_nid_postfix)
  • tgamma (tgamma_nid_postfix)
  • tgammaf (tgammaf_nid_postfix)

Implementation Details

  • Backed by std::lgamma and std::tgamma adhering to C99 standards and factorial/pole behaviors for integers and negative values.
  • Comprehensive unit test assertions added to core/libs/tests/GuestMath.cpp covering standard factorial values (e.g. tgamma(4) == 6), half-integers (tgamma(0.5) == sqrt(pi)), negative integer pole NaNs, and single/double precision log-gamma.

Tested

  • Unit tests in core/libs/tests/GuestMath.cpp (guest_math).
  • Clean 3-way merge against current upstream/main with 0 conflicts (tree: 290489a8ffbf8ea7f85a891a13b1e526931fcac7).
  • python tools/check_conventions.py --base 584cc4c57: All conventions checks passed.

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
  • Unblocks: PPSA01438
  • GPUs tested: n/a (libc math)
  • AI-assisted: yes

Co-authored-by: Nawasfadili nawasfadili@users.noreply.github.com

@github-actions

Copy link
Copy Markdown

Progress report (ab9a476 → fdbe668)

📈 System libraries: 88.72% (+0.01%, +3 implemented functions)

✅ 3 implemented
Library Name
libc lgammaf_nid_postfix
libc tgamma_nid_postfix
libc tgammaf_nid_postfix

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 8417 lgammaf
tgamma

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 6044 core/libs/tests/GuestMath.cpp
PR 6085 core/libs/prx/libc/src/Math.cpp
PR 6295 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 7201 core/libs/prx/libc/src/Math.cpp
PR 7544 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 8417 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 8658 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 8863 core/libs/prx/libc/src/Math.cpp
PR 8864 core/libs/prx/libc/src/Math.cpp
PR 8865 core/libs/tests/GuestMath.cpp
PR 8937 core/libs/prx/libc/src/Math.cpp
PR 9232 core/libs/prx/libc/src/Math.cpp
PR 9648 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9668 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9771 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9784 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9833 core/libs/tests/GuestMath.cpp
PR 9841 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9844 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9850 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9857 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9862 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9867 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 9873 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10001 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10002 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10003 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10007 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10113 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10222 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10226 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10229 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10234 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10245 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10247 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10250 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10253 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10265 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10274 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10283 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10306 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10314 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10315 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10316 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10317 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10321 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10325 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10328 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10345 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10346 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10349 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10354 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp

@wachi35676

Copy link
Copy Markdown
Contributor

On Windows, lgammaf_nid_postfix gives wrong results for small negative arguments, because it returns the host's std::lgamma(float). I checked at head fdbe66845 with a standalone program that calls std::lgamma(float) from a non-inlined function, the way this PR does, with the inputs passed through volatile so GCC can't fold them at compile time. I compared WinLibs GCC 15.2.0 (MinGW-w64, Windows 11) against glibc 2.39 (Ubuntu 24.04):

x Windows glibc (correct, ≈ -log|x|)
-1e-20f 46.05169 46.05170
-2.63e-23f 51.28646 51.99247
-2.58e-23f inf 52.01167
-1e-24f inf 55.26204
-1e-30f inf 69.07755
-1e-40f inf 92.10341
  • From about -1e-21 down, the Windows result drifts from the double-precision reference. The relative error reaches 1.3% near -2.67e-23.
  • From about -2.6e-23 down, the result is inf.
  • Positive tiny arguments and -0.5f are correct.

This is the float counterpart of #5775 (merged), which fixed the same MinGW behaviour for lgamma by returning -log|x| below a threshold, following FreeBSD's e_lgamma_r.c; FreeBSD's e_lgammaf_r.c has the same tiny-argument branch. A check such as std::abs(lgammaf_nid_postfix(-1e-30f) - 69.07755f) < 1e-4f would catch it in the Windows build. Linux passes either way, because glibc is correct here.

Also, the lgamma_nid_postfix context line in this branch predates #5775, so it will conflict with current main there.

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

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the PR. Changes needed:

  • core/libs/prx/libc/src/Math.cpp:251 lgammaf_nid_postfix returns the host std::lgamma(float), which is wrong on MinGW for tiny negative arguments (-2.6e-23f gives inf, see @wachi35676's measurements above). lgamma_nid_postfix on main already handles this (#5775: -log|x| below a threshold, following FreeBSD e_lgamma_r.c; FreeBSD's e_lgammaf_r.c has the same branch). Please do the same for the float version.
  • lgamma_nid_postfix on main also sets signgam_nid_postfix; lgammaf should set it the same way (FreeBSD lgammaf_r does).
  • Tests: add tiny-argument rows (±1e-30f, ±denorm_min) and a signgam check for lgammaf; with the current rows the Windows bug is invisible. For tgamma/tgammaf, exact == 6 style checks depend on the host libm (MinGW's tgamma is not guaranteed exact for integers); either use a tolerance or implement it from FreeBSD msun, and add a pole row (0, -0) next to the -1 NaN row.
  • The branch conflicts with main in Math.cpp and GuestMath.cpp (lgamma area changed by #5775); please rebase.
  • #6414 also adds tgamma/tgammaf; this PR came first, so that one should drop them.

@cadenasynyster

Copy link
Copy Markdown
Contributor

Measurement for the open review point on tgamma exactness: how MinGW's host tgamma/tgammaf does on integer arguments.

Windows 11, WinLibs GCC 15.2.0 r7 (x86_64-ucrt-posix-seh), -O0, arguments passed through volatile so nothing is folded at compile time. A standalone program, not this PR's code; it calls std::tgamma(double) and std::tgamma(float) and compares tgamma(n) with (n-1)!.

  • tgamma(double), n = 1..23: 12 of 23 are not exactly (n-1)!. Exact for n = 1-7, 12, 15, 20, 21; inexact for n = 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 22, 23. Examples: tgamma(8) = 5040.0000000000009, tgamma(11) = 3628799.9999999995, tgamma(19) = 6402373705727999 (exact 6402373705728000).
  • tgammaf(float), n = 1..14: 3 of 14 are not exact: tgammaf(6) = 119.999992 (exact 120), tgammaf(12) = 39916796 (exact 39916800), tgammaf(13) = 479001632 (exact 479001600).
  • tgamma(0.5) - sqrt(pi) = 2.22e-16 (not zero), so an == check against sqrt(pi) is not safe either.
  • Poles: tgamma(0) = inf, tgamma(-0.0) = -inf, tgamma(-1) = nan, tgammaf(0) = inf, tgammaf(-1) = nan.

So an exact tgamma(4) == 6 style check passes here only for small n; tolerances or an own implementation are needed for the rest, as the review says. I did not build this PR; only the host functions were measured. Not measured: Linux/glibc, other MinGW versions.

AI-assisted: yes (Claude Code).

@ionut27001

Copy link
Copy Markdown
Contributor

Console data for lgammaf, tgamma and tgammaf, in case it is useful here. p14 called the functions that libSceLibcInternal.sprx exports, from a payload process on a PS5 (firmware 12.70), with errno set to 0 before each call and signgam set to 777 before each lgammaf call:

p14: case lgammaf x=0x80000000 result=0x7f800000 signgam_after=1 errno=34
p14: case lgammaf x=0xc0200000 result=0xbd665fc8 signgam_after=-1 errno=0
p14: case lgammaf x=0xbf800000 result=0x7f800000 signgam_after=1 errno=34
p14: case tgamma x=0x8000000000000000 result=0xfff0000000000000 errno=0
p14: case tgammaf x=0x80000000 result=0xff800000 errno=0
p14: case tgamma x=0xbff0000000000000 result=0xfff8000000000000 errno=33
p14: case tgammaf x=0xbf800000 result=0xffc00000 errno=33
p14: case tgammaf x=0x432b8000 result=0x7f800000 errno=0

Compared with the host functions this PR calls, from a test program built with WinLibs GCC 15.2.0 on Windows:

  • The console's lgammaf writes signgam: 1 for -0 and -1, -1 for -2.5. This PR's lgammaf returns std::lgamma(x) and leaves signgam alone.
  • At the two poles measured, -0 and -1, the console's lgammaf sets errno to 34 (ERANGE); the host sets 33 (EDOM).
  • tgamma(-0) and tgammaf(-0) return -infinity with errno 0 on the console; the host sets 34.
  • tgammaf(171.5f) overflows to +infinity with errno 0 on the console; the host sets 34.
  • tgamma(-1) and tgammaf(-1) return a NaN with the sign bit set (0xfff8000000000000, 0xffc00000) and errno 33 on the console; the host's NaN has the sign bit clear.
  • tgamma(-2.5), tgammaf(-2.5f) and lgammaf(171.5f) differ from the console by 1 ulp on this host.

The probe source and the full log are in lgamma-probes.zip (p14). For lgamma itself, #7544 now sets errno to ERANGE after a pole and otherwise leaves it as it was; in p11 (same zip) the console's lgamma, called with errno set to 0, set 34 for 0, -0, -1, -2 and -1e306 and left it at 0 for 1e306, whose result overflows, both infinities and NaN.

AI-assisted: yes; the host results above were run on this machine.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing this in favour of #8417, which covers lgammaf, tgamma and tgammaf and merges cleanly with main. Thanks for the work here.

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

Labels

libc needs rebase Conflicts with main, rebase needed unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants