|
| 1 | +#include "config.h" |
| 2 | + |
| 3 | +namespace xsf { |
| 4 | + |
| 5 | +template <typename T> |
| 6 | +XSF_HOST_DEVICE typename std::enable_if<std::is_floating_point<T>::value, T>::type extended_absolute_error(T actual, T desired) { |
| 7 | + if (actual == desired || std::isnan(actual) && std::isnan(desired)) { |
| 8 | + return T(0); |
| 9 | + } |
| 10 | + if (std::isnan(desired) || std::isnan(actual)) { |
| 11 | + /* If expected nan but got non-NaN or expected non-NaN but got NaN |
| 12 | + * we consider this to be an infinite error. */ |
| 13 | + return std::numeric_limits<T>::infinity(); |
| 14 | + } |
| 15 | + if (std::isinf(actual)) { |
| 16 | + /* We don't want to penalize early overflow too harshly, so instead |
| 17 | + * compare with the mythical value nextafter(max_float). */ |
| 18 | + T sgn = std::copysign(1.0, actual); |
| 19 | + T max_float = std::numeric_limits<T>::max(); |
| 20 | + // max_float * 2**-(mantissa_bits + 1) = ulp(max_float) |
| 21 | + T ulp = std::pow(2, -std::numeric_limits<T>::digits) * max_float; |
| 22 | + return std::abs((sgn * std::numeric_limits<T>::max() - desired) + sgn * ulp); |
| 23 | + } |
| 24 | + if (std::isinf(desired)) { |
| 25 | + T sgn = std::copysign(1.0, desired); |
| 26 | + T max_float = std::numeric_limits<T>::max(); |
| 27 | + // max_float * 2**-(mantissa_bits + 1) = ulp(max_float) |
| 28 | + T ulp = std::pow(2, -std::numeric_limits<T>::digits) * max_float; |
| 29 | + return std::abs((sgn * std::numeric_limits<T>::max() - actual) + sgn * ulp); |
| 30 | + } |
| 31 | + return std::abs(actual - desired); |
| 32 | +} |
| 33 | + |
| 34 | + |
| 35 | +template <typename T> |
| 36 | +XSF_HOST_DEVICE T extended_absolute_error(std::complex<T> actual, std::complex<T> desired) { |
| 37 | + return std::hypot(extended_absolute_error(actual.real(), desired.real()), extended_absolute_error(actual.imag(), desired.imag())); |
| 38 | +} |
| 39 | + |
| 40 | + |
| 41 | +template <typename T> |
| 42 | +XSF_HOST_DEVICE typename std::enable_if<std::is_floating_point<T>::value, T>::type extended_relative_error(T actual, T desired) { |
| 43 | + T abs_error = extended_absolute_error(actual, desired); |
| 44 | + T abs_desired = std::abs(desired); |
| 45 | + if (desired == 0.0) { |
| 46 | + /* If the desired result is 0.0, normalize by smallest subnormal instead |
| 47 | + * of zero. */ |
| 48 | + abs_desired = std::numeric_limits<T>::denorm_min(); |
| 49 | + } else if (std::isinf(desired)) { |
| 50 | + abs_desired = std::numeric_limits<T>::max(); |
| 51 | + } else if (std::isnan(desired)) { |
| 52 | + /* This ensures extended_relative_error(nan, nan) = 0 but |
| 53 | + * extended_relative_error(x0, x1) is infinite if one but not both of |
| 54 | + * x0 and x1 equals NaN */ |
| 55 | + abs_desired = T(1); |
| 56 | + } |
| 57 | + return abs_error / abs_desired; |
| 58 | +} |
| 59 | + |
| 60 | + |
| 61 | +template <typename T> |
| 62 | +XSF_HOST_DEVICE T extended_relative_error(std::complex<T> actual, std::complex<T> desired) { |
| 63 | + T abs_error = extended_absolute_error(actual, desired); |
| 64 | + |
| 65 | + if (desired.real() == 0.0) { |
| 66 | + desired.real(std::copysign(std::numeric_limits<T>::denorm_min(), desired.real())); |
| 67 | + } else if (std::isinf(desired.real())) { |
| 68 | + desired.real(std::copysign(std::numeric_limits<T>::max(), desired.real())); |
| 69 | + } else if (std::isnan(desired.real())) { |
| 70 | + /* In this case, the value used for desired doesn't matter. If desired.real() is NaN |
| 71 | + * but actual.real() isn't NaN, then the extended_absolute_error will be inf already |
| 72 | + * anyway. */ |
| 73 | + desired.real(1.0); |
| 74 | + } |
| 75 | + |
| 76 | + if (desired.imag() == 0.0) { |
| 77 | + desired.imag(std::copysign(std::numeric_limits<T>::denorm_min(), desired.imag())); |
| 78 | + } else if (std::isinf(desired.imag())) { |
| 79 | + desired.imag(std::copysign(std::numeric_limits<T>::max(), desired.imag())); |
| 80 | + } else if (std::isnan(desired.imag())) { |
| 81 | + /* In this case, the value used for desired doesn't matter. If desired.imag() is NaN |
| 82 | + * but actual.imag() isn't NaN, then the extended_absolute_error will be inf already |
| 83 | + * anyway. */ |
| 84 | + desired.imag(1.0); |
| 85 | + } |
| 86 | + |
| 87 | + if (!(std::isinf(desired.real()) || std::isinf(desired.imag())) && std::isinf(std::abs(desired))) { |
| 88 | + /* Rescale to avoid overflow */ |
| 89 | + return (abs_error / 2.0) / (std::abs(desired / 2.0)); |
| 90 | + } |
| 91 | + |
| 92 | + return abs_error / abs(desired); |
| 93 | +} |
| 94 | + |
| 95 | +} |
0 commit comments