@@ -149,3 +149,89 @@ 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
+ TAxis axis (2000 , -1000 ., 1000 .);
170
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-1000 ., -2000 .)), 0 );
171
+ EXPECT_EQ (axis.FindFixBin (-1000 .), 1 );
172
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-1000 ., 0 )), 1 );
173
+
174
+ EXPECT_EQ (axis.FindFixBin (-500.00000000001 ), 500 );
175
+ EXPECT_EQ (axis.FindFixBin (-500 .), 501 );
176
+ EXPECT_EQ (axis.FindFixBin (-499.9 ), 501 );
177
+
178
+ EXPECT_EQ (axis.FindFixBin (-1 .E -20 ), 1000 );
179
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (-0 ., -1 )), 1000 );
180
+ EXPECT_EQ (axis.FindFixBin (-0 .), 1001 );
181
+ EXPECT_EQ (axis.FindFixBin (0 .), 1001 );
182
+
183
+ EXPECT_EQ (axis.FindFixBin (499.9 ), 1500 );
184
+ EXPECT_EQ (axis.FindFixBin (500 .), 1501 );
185
+
186
+ EXPECT_EQ (axis.FindFixBin (1000 . - 1 .E -13 ), 2000 );
187
+ EXPECT_EQ (axis.FindFixBin (std::nextafter (1000 ., 0 .)), 2000 );
188
+ EXPECT_EQ (axis.FindFixBin (1000 .), 2001 );
189
+
190
+ EXPECT_EQ (axis.FindBin (std::nextafter (-1000 ., -2000 .)), 0 );
191
+ EXPECT_EQ (axis.FindBin (-1000 .), 1 );
192
+ EXPECT_EQ (axis.FindBin (std::nextafter (-1000 ., 0 )), 1 );
193
+
194
+ EXPECT_EQ (axis.FindBin (-500.00000000001 ), 500 );
195
+ EXPECT_EQ (axis.FindBin (-500 .), 501 );
196
+ EXPECT_EQ (axis.FindBin (-499.9 ), 501 );
197
+
198
+ EXPECT_EQ (axis.FindBin (-1 .E -20 ), 1000 );
199
+ EXPECT_EQ (axis.FindBin (std::nextafter (-0 ., -1 )), 1000 );
200
+ EXPECT_EQ (axis.FindBin (-0 .), 1001 );
201
+ EXPECT_EQ (axis.FindBin (0 .), 1001 );
202
+
203
+ EXPECT_EQ (axis.FindBin (499.9 ), 1500 );
204
+ EXPECT_EQ (axis.FindBin (500 .), 1501 );
205
+
206
+ EXPECT_EQ (axis.FindBin (1000 . - 1 .E -13 ), 2000 );
207
+ EXPECT_EQ (axis.FindBin (std::nextafter (1000 ., 0 .)), 2000 );
208
+ EXPECT_EQ (axis.FindBin (1000 .), 2001 );
209
+ }
210
+
211
+ for (const auto & [low, high] : std::initializer_list<std::pair<long double , long double >>{{-10654 .1l , 32165 .l },
212
+ {-1 .0656E23l, -20654 .l },
213
+ {1 .1234E4l, 4 .5678E20l},
214
+ {1 .E -60l , 1 .E -20l },
215
+ {-1 .E -20l , -1 .E -60l }}) {
216
+ constexpr int N = 100 ;
217
+ const double width = (high - low) / N;
218
+ std::mt19937 gen;
219
+ std::uniform_real_distribution dist{low - width, high + width};
220
+ TAxis axis (N, low, high);
221
+ for (unsigned int i = 0 ; i < 100000 ; ++i) {
222
+ long double x = dist (gen);
223
+ if (i == 0 ) {
224
+ x = std::nextafter (double (high), double (low));
225
+ }
226
+ const auto bin = axis.FindFixBin (x);
227
+ if (x < double (low))
228
+ EXPECT_EQ (bin, 0 );
229
+ else
230
+ EXPECT_EQ (bin, 1 + int ((x - low) / width))
231
+ << x << " [" << low << " ," << high << " ]" ; // Note: Bin computation in long double here
232
+ EXPECT_LE (axis.GetBinLowEdge (bin), x);
233
+ EXPECT_LT (x, axis.GetBinUpEdge (bin));
234
+ EXPECT_EQ (bin, axis.FindBin (x));
235
+ }
236
+ }
237
+ }
0 commit comments