| 173 | |
| 174 | template <typename AccumScalar, typename DstScalar> |
| 175 | bool CheckErrorStats(const ErrorStats& error_stats, int accumulation_depth) { |
| 176 | double tolerated_relative_max_abs_diff = 0; |
| 177 | double tolerated_relative_mean_abs_diff = 0; |
| 178 | double tolerated_relative_abs_mean_diff = 0; |
| 179 | |
| 180 | double inverse_size = 1. / error_stats.size; |
| 181 | |
| 182 | if (std::is_floating_point<AccumScalar>::value) { |
| 183 | // Somewhat naive requirement: the worst case should be epsilons |
| 184 | // adding up towards the same direction, on values of same magnitude. |
| 185 | tolerated_relative_max_abs_diff = |
| 186 | accumulation_depth * std::numeric_limits<DstScalar>::epsilon(); |
| 187 | // Naive interpretation of the Central Limit Theorem is the rationale |
| 188 | // for the sqrt here. We haven't even worked out the correct scale factor, |
| 189 | // or how applicable that theorem is here (the random variables being added |
| 190 | // might not be mutually independent). |
| 191 | tolerated_relative_mean_abs_diff = |
| 192 | std::sqrt(static_cast<double>(accumulation_depth)) * |
| 193 | std::numeric_limits<DstScalar>::epsilon(); |
| 194 | // Unbiasing requirement: we require the bias, abs_mean_diff, to be much |
| 195 | // smaller than the mean_abs_diff, except when there are very few values. |
| 196 | tolerated_relative_abs_mean_diff = |
| 197 | tolerated_relative_mean_abs_diff * std::sqrt(inverse_size); |
| 198 | } else { |
| 199 | // In quantized arithmetic, tolerate minor rounding differences, resulting |
| 200 | // in off-by-one errors (tolerated_relative_max_abs_diff = 1), as long |
| 201 | // as they are rare (tolerated_relative_mean_abs_diff) and unbiased |
| 202 | // (tolerated_relative_abs_mean_diff). |
| 203 | tolerated_relative_max_abs_diff = 1; |
| 204 | // Naively require mean_abs_diff and abs_mean_diff to converge to zero |
| 205 | // as size gets large. We don't know at all how quick that convergence |
| 206 | // should be: this is just based on trial-and-error and striking a |
| 207 | // compromise between something that works and something that's simple |
| 208 | // enough code that doesn't feel too ad-hoc. As above in the float path, |
| 209 | // abs_mean_diff is subject to a stricter requirement as it is a bias. |
| 210 | tolerated_relative_mean_abs_diff = std::sqrt(inverse_size); |
| 211 | tolerated_relative_abs_mean_diff = inverse_size; |
| 212 | } |
| 213 | |
| 214 | double tolerated_max_abs_diff = |
| 215 | tolerated_relative_max_abs_diff * error_stats.scale_factor; |
| 216 | double tolerated_mean_abs_diff = |
| 217 | tolerated_relative_mean_abs_diff * error_stats.scale_factor; |
| 218 | double tolerated_abs_mean_diff = |
| 219 | tolerated_relative_abs_mean_diff * error_stats.scale_factor; |
| 220 | |
| 221 | EXPECT_LE(error_stats.max_abs_diff, tolerated_max_abs_diff); |
| 222 | EXPECT_LE(error_stats.mean_abs_diff, tolerated_mean_abs_diff); |
| 223 | EXPECT_LE(error_stats.abs_mean_diff, tolerated_abs_mean_diff); |
| 224 | |
| 225 | return error_stats.max_abs_diff <= tolerated_max_abs_diff && |
| 226 | error_stats.mean_abs_diff <= tolerated_mean_abs_diff && |
| 227 | error_stats.abs_mean_diff <= tolerated_abs_mean_diff; |
| 228 | } |
| 229 | |
| 230 | template <typename AccumScalar, typename DstScalar> |
| 231 | void CheckErrorForAccumulation(int accumulation_depth, |