Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README/ReleaseNotes/v642/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ maps) will now obtain different, mathematically consistent values.

## Math

## Trees

### Behavior change: `sqrt()` of negative arguments in TTreeFormula now returns NaN

Since its introduction in 1995, the formula engine used by `TTree::Draw()`, `TTree::Scan()` and `TTreeFormula`
silently evaluated `sqrt(x)` as `sqrt(abs(x))` for negative arguments (or as `0` in the optimized evaluation path
of the legacy `ROOT::v5::TFormula`). This could produce silently wrong results, e.g. in selections involving
`sqrt` of an expression that can become negative. `sqrt()` now returns NaN for negative arguments, consistent
with `TMath::Sqrt()`, the standard C `sqrt()`, and the modern `TFormula` used by `TF1`.
Note that in a selection, a NaN evaluates as `false`, so entries where the `sqrt` argument is negative now fail
the cut instead of being selected based on `sqrt(abs(x))`.

## RooFit

### Removal of the the constant term optimization for legacy test statistic classes
Expand Down
2 changes: 1 addition & 1 deletion hist/hist/src/TFormula_v5.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2764,7 +2764,7 @@ Double_t TFormula::EvalParOld(const Double_t *x, const Double_t *uparams)
case kfmod : pos--; tab[pos-1] = fmod(tab[pos-1],tab[pos]); continue;
case kpow : pos--; tab[pos-1] = TMath::Power(tab[pos-1],tab[pos]); continue;
case ksq : tab[pos-1] = tab[pos-1]*tab[pos-1]; continue;
case ksqrt : tab[pos-1] = TMath::Sqrt(TMath::Abs(tab[pos-1])); continue;
case ksqrt : tab[pos-1] = TMath::Sqrt(tab[pos-1]); continue;

case kstrstr : strpos -= 2; pos-=2; pos++;
if (strstr(stringStack[strpos],stringStack[strpos+1])) tab[pos-1]=1;
Expand Down
2 changes: 1 addition & 1 deletion tree/treeplayer/src/TTreeFormula.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4259,7 +4259,7 @@ T TTreeFormula::EvalInstance(Int_t instance, const char *stringStackArg[])
case kfmod : pos--; tab[pos-1] = fmod_local(tab[pos-1],tab[pos]); continue;
case kpow : pos--; tab[pos-1] = TMath::Power(tab[pos-1],tab[pos]); continue;
case ksq : tab[pos-1] = tab[pos-1]*tab[pos-1]; continue;
case ksqrt : tab[pos-1] = TMath::Sqrt(TMath::Abs(tab[pos-1])); continue;
case ksqrt : tab[pos-1] = TMath::Sqrt(tab[pos-1]); continue;

case kstrstr : pos2 -= 2; pos++;if (strstr(stringStack[pos2],stringStack[pos2+1])) tab[pos-1]=1;
else tab[pos-1]=0;
Expand Down
18 changes: 18 additions & 0 deletions tree/treeplayer/test/regressions.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,21 @@ TEST(TTreeScan, ULong64Precision)
EXPECT_EQ(scanToString("colsize=21 col=lld:lld:lld"), expectedScanOut);
EXPECT_EQ(scanToString("col=21lld:21lld:21lld"), expectedScanOut);
}

// https://github.com/root-project/root/issues/22755
// sqrt() of a negative argument used to evaluate to sqrt(abs(x)) instead of NaN
TEST(TTreeFormulaRegressions, SqrtOfNegative)
{
TTree t("t", "t");
int x = 0;
t.Branch("x", &x, "x/I");
t.Fill();
t.GetEntry(0);

TTreeFormula tf("tf", "sqrt(-4.0)", &t);
EXPECT_TRUE(std::isnan(tf.EvalInstance()));
TTreeFormula tf2("tf2", "sqrt(x - 4)", &t);
EXPECT_TRUE(std::isnan(tf2.EvalInstance()));
TTreeFormula tf3("tf3", "sqrt(x + 9)", &t);
EXPECT_FLOAT_EQ(tf3.EvalInstance(), 3.);
}
Loading