Skip to content

[feature](function) Support gamma scalar function - #68029

Open
RH211-sys wants to merge 1 commit into
apache:masterfrom
RH211-sys:feat-gamma-function
Open

RH211-sys wants to merge 1 commit into
apache:masterfrom
RH211-sys:feat-gamma-function

Conversation

@RH211-sys

@RH211-sys RH211-sys commented Sep 15, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Related issue: #48203

Related PR: #51576, #60111 — earlier attempts at the same function, both closed without merging.
This one additionally implements FE constant folding that agrees with the BE at the poles and above
the commons-math3 overflow point, ships a regression suite covering the same case classes as
test_template_one_arg.groovy, and links a documentation PR.

Problem Summary:

Add the gamma scalar function, which generalizes the factorial to real numbers: gamma(n) is
(n - 1)! for a positive integer n, and gamma(0.5) is sqrt(pi).

  • BE: gamma is registered in be/src/exprs/function/math.cpp on top of std::tgamma. The poles
    are mapped to NULL instead of the value the C library produces: gamma(0) and every negative
    integer return NULL, and so does negative infinity, which the BE classifies as a negative
    integer pole. A NaN argument returns NaN, and positive infinity or an argument large enough to
    overflow a double (gamma(172) and above) returns Infinity.
  • FE: Gamma (unary, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral),
    the Nereids visitor entry, and the builtin scalar function registration.
  • FE constant folding: NumericArithmetic.gamma, so that a folded gamma(<literal>) produces the
    same value as the BE. commons-math3's Gamma.gamma saturates to Infinity at 165, where
    std::tgamma still returns a finite 3.29e293 (it stays finite up to 171), so positive inputs are
    evaluated as exp(logGamma(x)). Negative non-integers have no overflow problem and use
    Gamma.gamma.

Release note

Add the gamma scalar function. gamma(n) is (n - 1)! for a positive integer n; gamma(0)
and the negative integers return NULL.

Check List (For Author)

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

Manual test:

  • select gamma(5), gamma(0.5), gamma(-1.5), gamma(0), gamma(-1), gamma(-3); returns
    24.000000000000004, 1.772453850905516, 2.3632718012073544, NULL, NULL, NULL, and the same
    values are returned with set debug_skip_fold_constant=true, i.e. constant folding and BE
    execution agree.
  • gamma(cast('nan' as double)) is NaN, gamma(cast('inf' as double)) is Infinity,
    gamma(cast('-inf' as double)) is NULL, gamma(165) is 3.287218585534318E293,
    gamma(171) is 7.257415615308056E306 and gamma(172) is Infinity.
  • explain select gamma(v) from t where gamma(v) > 10; still plans an olap scan with the
    predicate and the projection applied.

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

### What problem does this PR solve?

Related issue: [apache#48203](apache#48203)

Problem Summary:

Add the `gamma` scalar function, which generalizes the factorial to real numbers: `gamma(n)` is
`(n - 1)!` for a positive integer `n`, and `gamma(0.5)` is `sqrt(pi)`.

- BE: `gamma` is registered in `be/src/exprs/function/math.cpp` on top of `std::tgamma`. The poles
  are mapped to NULL instead of the value the C library produces: `gamma(0)` and every negative
  integer return NULL, and so does negative infinity, which the BE classifies as a negative
  integer pole. A NaN argument returns NaN, and positive infinity or an argument large enough to
  overflow a double (`gamma(172)` and above) returns Infinity.
- FE: `Gamma` (unary, `ExplicitlyCastableSignature`, `AlwaysNullable`, `PropagateNullLiteral`),
  the Nereids visitor entry, and the builtin scalar function registration.
- FE constant folding: `NumericArithmetic.gamma`, so that a folded `gamma(<literal>)` produces the
  same value as the BE. commons-math3's `Gamma.gamma` saturates to Infinity at 165, where
  `std::tgamma` still returns a finite 3.29e293 (it stays finite up to 171), so positive inputs are
  evaluated as `exp(logGamma(x))`. Negative non-integers have no overflow problem and use
  `Gamma.gamma`.

### Release note

Add the `gamma` scalar function. `gamma(n)` is `(n - 1)!` for a positive integer `n`; `gamma(0)`
and the negative integers return NULL.

### Check List (For Author)

- Test
    - [x] Regression test
    - [x] Unit Test
    - [x] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- Behavior changed:
    - [x] No.
    - [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
    - [ ] No.
    - [x] Yes. <!-- REPLACE THIS WITH THE DOCS PR LINK, e.g. https://github.com/apache/doris-website/pull/XXXX -->

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label

Manual test:

- `select gamma(5), gamma(0.5), gamma(-1.5), gamma(0), gamma(-1), gamma(-3);` returns
  `24.000000000000004, 1.772453850905516, 2.3632718012073544, NULL, NULL, NULL`, and the same
  values are returned with `set debug_skip_fold_constant=true`, i.e. constant folding and BE
  execution agree.
- `gamma(cast('nan' as double))` is NaN, `gamma(cast('inf' as double))` is Infinity,
  `gamma(cast('-inf' as double))` is NULL, `gamma(165)` is 3.287218585534318E293,
  `gamma(171)` is 7.257415615308056E306 and `gamma(172)` is Infinity.
- `explain select gamma(v) from t where gamma(v) > 10;` still plans an olap scan with the
  predicate and the projection applied.
@RH211-sys

Copy link
Copy Markdown
Author

Friendly ping @morrySnow @924060929 @englefly @starocean999 (Code Owners of
fe/fe-core/src/main/java/org/apache/doris/nereids) and @yiguolei (recent committer on
be/src/exprs/function/math.cpp).

This PR adds the gamma scalar function. As a first-time contributor the CI workflows are still
waiting for approval, so no checks have started yet; I also posted a request to
dev@doris.apache.org. Could someone approve the workflows and take a look?

Local validation: full sh build.sh --be --fe on the dev branch, BE UT
MathFunctionTest.gamma_test passes, the new regression suite test_gamma passes in compare mode,
and the folded/unfolded values agree.

Docs PR: apache/doris-website#4140

Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants