| 373 | private: |
| 374 | template <typename T> |
| 375 | std::pair<float, int32_t> QuantizationParams(float f_min, float f_max) { |
| 376 | int32_t zero_point = 0; |
| 377 | float scale = 0; |
| 378 | const T qmin = std::numeric_limits<T>::min(); |
| 379 | const T qmax = std::numeric_limits<T>::max(); |
| 380 | const float qmin_double = qmin; |
| 381 | const float qmax_double = qmax; |
| 382 | // 0 should always be a representable value. Let's assume that the initial |
| 383 | // min,max range contains 0. |
| 384 | CHECK_LE(f_min, 0); |
| 385 | CHECK_GE(f_max, 0); |
| 386 | if (f_min == f_max) { |
| 387 | // Special case where the min,max range is a point. Should be {0}. |
| 388 | CHECK_EQ(f_min, 0); |
| 389 | CHECK_EQ(f_max, 0); |
| 390 | return {scale, zero_point}; |
| 391 | } |
| 392 | |
| 393 | // General case. |
| 394 | // |
| 395 | // First determine the scale. |
| 396 | scale = (f_max - f_min) / (qmax_double - qmin_double); |
| 397 | |
| 398 | // Zero-point computation. |
| 399 | // First the initial floating-point computation. The zero-point can be |
| 400 | // determined from solving an affine equation for any known pair |
| 401 | // (real value, corresponding quantized value). |
| 402 | // We know two such pairs: (rmin, qmin) and (rmax, qmax). |
| 403 | // The arithmetic error on the zero point computed from either pair |
| 404 | // will be roughly machine_epsilon * (sum of absolute values of terms) |
| 405 | // so we want to use the variant that adds the smaller terms. |
| 406 | const float zero_point_from_min = qmin_double - f_min / scale; |
| 407 | const float zero_point_from_max = qmax_double - f_max / scale; |
| 408 | |
| 409 | const float zero_point_from_min_error = |
| 410 | std::abs(qmin_double) + std::abs(f_min / scale); |
| 411 | |
| 412 | const float zero_point_from_max_error = |
| 413 | std::abs(qmax_double) + std::abs(f_max / scale); |
| 414 | |
| 415 | const float zero_point_double = |
| 416 | zero_point_from_min_error < zero_point_from_max_error |
| 417 | ? zero_point_from_min |
| 418 | : zero_point_from_max; |
| 419 | |
| 420 | // Now we need to nudge the zero point to be an integer |
| 421 | // (our zero points are integer, and this is motivated by the requirement |
| 422 | // to be able to represent the real value "0" exactly as a quantized value, |
| 423 | // which is required in multiple places, for example in Im2col with SAME |
| 424 | // padding). |
| 425 | |
| 426 | T nudged_zero_point = 0; |
| 427 | if (zero_point_double < qmin_double) { |
| 428 | nudged_zero_point = qmin; |
| 429 | } else if (zero_point_double > qmax_double) { |
| 430 | nudged_zero_point = qmax; |
| 431 | } else { |
| 432 | nudged_zero_point = static_cast<T>(std::round(zero_point_double)); |