Floating-point rounding error when filling the histogram


I have a histogram and would like to know which bin a given double will be attributed to:

root [0] TH1F* myHisto = new TH1F("myHisto", "Title;X;Y", 128, -0.352, 0.352);
root [1] printf("\tBin #61: from %.20f to %.20f, but somehow %.20f goes into bin #%i - why?\n", myHisto->GetXaxis()->GetBinLowEdge(61), myHisto->GetXaxis()->GetBinUpEdge(61), (double)-0.0220, myHisto->FindBin((double)-0.0220));
        Bin #61: from -0.02200000000000001954 to -0.01650000000000001465, but somehow -0.02199999999999999872 goes into bin #60 - why?

Why is this happening? I realize by now it has to do with the floating-point error (and it’s not a ROOT-specific question per se) and I know how to “fix” the issue (or “hide” it if you will), but I just can’t put my finger on where exactly that rounding error is in this case. Do you mind explaining?

P.S. If I switch from double to a float (see below), the result starts to make sense… But what does this precision loss have to do with it? The lower boundary of the bin is not between (float)-0.0220 and (double)-0.0220 anyway…

root [2] printf("\tBin #61: %.20f cm to %.20f cm, and %.20f goes into bin #%i\n", myHisto->GetXaxis()->GetBinLowEdge(61), myHisto->GetXaxis()->GetBinUpEdge(61), (float)-0.0220, myHisto->FindBin((float)-0.0220));
        Bin #61: -0.02200000000000001954 cm to -0.01650000000000001465 cm, and -0.02199999988079071045 goes into bin #61


ROOT Version: ROOT 6.14/04
Platform: x86_64-slc6-gcc62-opt
Compiler: gcc62

Histogram axes (“binning”) in ROOT always use “double” (even if histogram “bin contents” use “float”).

properly answering your question would require digging into the callstack to check how exactly FindBin evaluates bin number from x value. Maybe @moneta can tell from the top of his head (but he is currently traveling).

Note however that you are filling the histogram with a value that is “precisely” on the bin edge and since floating point equality is tricky at best, I would not expect any consistent result e.g. between different architectures. If by pure chance your analysis results depend on these finite precision effects, I suggest you shift the bin edges slightly.

In short, this is the effect:

root [0] TH1F h("h", "h", 128, -0.352, 0.352);
root [1] h.GetXaxis()->GetBinUpEdge(60)
(double) -0.022000000
root [2] h.FindBin(-0.0220)
(int) 60
root [3] h.FindBin(-0.02199999999999999)
(int) 61
root [4] h.FindBin(-0.021999999999999999) // one digit more
(int) 60 


This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.