Skip to content
Merged
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
10 changes: 10 additions & 0 deletions README/ReleaseNotes/v640/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ This change affects the following classes: `TFile`, `TMapFile`, `TMemFile`, `TD

### TTree

#### Behavior change since 6.40.06: `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))`.

### RNTuple

- A new API to create "active entry tokens" was added to the `RNTupleReader`. Active entry tokens that are used prevent cache eviction of the entries at hand.
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