@@ -149,3 +149,101 @@ TEST(TH1, Normalize)
149
149
EXPECT_FLOAT_EQ (v2.Integral (" width" ), 1 .);
150
150
EXPECT_FLOAT_EQ (v2.GetMaximum (), 7.9999990 );
151
151
}
152
+
153
+ TEST (TAxis, BinComputation_FPAccuracy)
154
+ {
155
+ // Example from 1703c54
156
+ EXPECT_EQ (TAxis (1 , -1 ., 0 .).FindBin (-1e-17 ), 1 );
157
+
158
+ {
159
+ // https://root-forum.cern.ch/t/floating-point-rounding-error-when-filling-the-histogram/35189
160
+ TAxis axis (128 , -0.352 , 0.352 );
161
+ constexpr double x = -0.0220 ;
162
+ EXPECT_EQ (axis.FindBin (x), 61 );
163
+ EXPECT_EQ (axis.FindFixBin (x), 61 );
164
+ EXPECT_LE (axis.GetBinLowEdge (61 ), x);
165
+ EXPECT_GT (axis.GetBinUpEdge (61 ), x);
166
+ }
167
+
168
+ {
169
+ // https://github.com/root-project/root/issues/14091
170
+ constexpr int nBins = 30 ;
171
+ constexpr double xMin=3.0 , xMax=6.0 ;
172
+ TAxis ax (nBins, xMin, xMax);
173
+
174
+ for (Int_t i=1 ; i <= ax.GetNbins (); i++) {
175
+ EXPECT_EQ (i, ax.FindBin (ax.GetBinLowEdge (i)));
176
+ }
177
+ }
178
+
179
+ {
180
+ TAxis axis (2000 , -1000 ., 1000 .);
181
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-1000 ., -2000 .)), 0 );
182
+ EXPECT_EQ (axis.FindFixBin (-1000 .), 1 );
183
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-1000 ., 0 )), 1 );
184
+
185
+ EXPECT_EQ (axis.FindFixBin (-500.00000000001 ), 500 );
186
+ EXPECT_EQ (axis.FindFixBin (-500 .), 501 );
187
+ EXPECT_EQ (axis.FindFixBin (-499.9 ), 501 );
188
+
189
+ EXPECT_EQ (axis.FindFixBin (-1 .E -20 ), 1000 );
190
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-0 ., -1 )), 1000 );
191
+ EXPECT_EQ (axis.FindFixBin (-0 .), 1001 );
192
+ EXPECT_EQ (axis.FindFixBin (0 .), 1001 );
193
+
194
+ EXPECT_EQ (axis.FindFixBin (499.9 ), 1500 );
195
+ EXPECT_EQ (axis.FindFixBin (500 .), 1501 );
196
+
197
+ EXPECT_EQ (axis.FindFixBin (1000 . - 1 .E -13 ), 2000 );
198
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (1000 ., 0 .)), 2000 );
199
+ EXPECT_EQ (axis.FindFixBin (1000 .), 2001 );
200
+
201
+ EXPECT_EQ (axis.FindBin (std::nextafter (-1000 ., -2000 .)), 0 );
202
+ EXPECT_EQ (axis.FindBin (-1000 .), 1 );
203
+ EXPECT_EQ (axis.FindBin (std::nextafter (-1000 ., 0 )), 1 );
204
+
205
+ EXPECT_EQ (axis.FindBin (-500.00000000001 ), 500 );
206
+ EXPECT_EQ (axis.FindBin (-500 .), 501 );
207
+ EXPECT_EQ (axis.FindBin (-499.9 ), 501 );
208
+
209
+ EXPECT_EQ (axis.FindBin (-1 .E -20 ), 1000 );
210
+ EXPECT_EQ (axis.FindBin (std::nextafter (-0 ., -1 )), 1000 );
211
+ EXPECT_EQ (axis.FindBin (-0 .), 1001 );
212
+ EXPECT_EQ (axis.FindBin (0 .), 1001 );
213
+
214
+ EXPECT_EQ (axis.FindBin (499.9 ), 1500 );
215
+ EXPECT_EQ (axis.FindBin (500 .), 1501 );
216
+
217
+ EXPECT_EQ (axis.FindBin (1000 . - 1 .E -13 ), 2000 );
218
+ EXPECT_EQ (axis.FindBin (std::nextafter (1000 ., 0 .)), 2000 );
219
+ EXPECT_EQ (axis.FindBin (1000 .), 2001 );
220
+ }
221
+
222
+ for (const auto & [low, high] : std::initializer_list<std::pair<long double , long double >>{{-10654 .1l , 32165 .l },
223
+ {-1 .0656E23l, -20654 .l },
224
+ {1 .1234E4l, 4 .5678E20l},
225
+ {1 .E -60l , 1 .E -20l },
226
+ {-1 .E -20l , -1 .E -60l }}) {
227
+ constexpr int N = 100 ;
228
+ const double width = (high - low) / N;
229
+ std::mt19937 gen;
230
+ std::uniform_real_distribution dist{low - width, high + width};
231
+ TAxis axis (N, low, high);
232
+ for (unsigned int i = 0 ; i < 100000 ; ++i) {
233
+ long double x = dist (gen);
234
+ if (i == 0 ) {
235
+ x = std::nextafter (double (high), double (low));
236
+ }
237
+ const auto bin = axis.FindFixBin (x);
238
+ EXPECT_EQ (bin, axis.FindBin (x));
239
+
240
+ if (x < double (low))
241
+ EXPECT_EQ (bin, 0 );
242
+ else if (x >= double (high))
243
+ EXPECT_EQ (bin, N+1 );
244
+
245
+ EXPECT_LE (axis.GetBinLowEdge (bin), x);
246
+ EXPECT_LT (x, axis.GetBinUpEdge (bin));
247
+ }
248
+ }
249
+ }
0 commit comments