@@ -149,3 +149,132 @@ 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
+ }
250
+
251
+ // The tests below are taken from https://github.com/root-project/root/pull/14105
252
+ TEST (TAxis, FindBinExact)
253
+ {
254
+ // Test the case where bin edges are exactly represented as floating points
255
+ TAxis ax (88 , 1010 , 1098 );
256
+ for (int i = 1 ; i <= ax.GetNbins (); i++) {
257
+ double x = ax.GetBinLowEdge (i);
258
+ EXPECT_EQ (i, ax.FindBin (x));
259
+ EXPECT_EQ (i, ax.FindFixBin (x));
260
+ x = ax.GetBinUpEdge (i);
261
+ EXPECT_EQ (i + 1 , ax.FindBin (x));
262
+ EXPECT_EQ (i + 1 , ax.FindFixBin (x));
263
+ x -= x * std::numeric_limits<double >::epsilon ();
264
+ EXPECT_EQ (i, ax.FindBin (x));
265
+ }
266
+ }
267
+ TEST (TAxis, FindBinApprox)
268
+ {
269
+ TAxis ax (90 , 0 ., 10 .);
270
+ for (int i = 1 ; i <= ax.GetNbins (); i++) {
271
+ double x = ax.GetBinLowEdge (i);
272
+ EXPECT_EQ (i, ax.FindBin (x));
273
+ EXPECT_EQ (i, ax.FindFixBin (x));
274
+ x = ax.GetBinUpEdge (i);
275
+ EXPECT_EQ (i + 1 , ax.FindBin (x));
276
+ EXPECT_EQ (i + 1 , ax.FindFixBin (x));
277
+ x -= x * std::numeric_limits<double >::epsilon ();
278
+ EXPECT_EQ (i, ax.FindBin (x));
279
+ }
280
+ }
0 commit comments