fix fmin and fmax NaN handling fmin and fmax do not handle NaNs correctly. Also, the comparison operator for floating-point numbers may raise FE_INVALID if one of the arguments is NaN. Both functions, however, are not subject to any of the error conditions specified by the error handling for related floating-point operators and functions. Change-Id: Ic6bb65f18568066dba3c739a2df06f5fc3131a80
diff --git a/include/ceres/jet.h b/include/ceres/jet.h index e45caf2..38a69f4 100644 --- a/include/ceres/jet.h +++ b/include/ceres/jet.h
@@ -632,28 +632,34 @@ template <typename T, int N> inline Jet<T, N> fmax(const Jet<T, N>& x, const Jet<T, N>& y) { - return x < y ? y : x; + using std::isgreater; + return isnan(y.a) || isgreater(x.a, y.a) ? x : y; } + template <typename T, int N> inline Jet<T, N> fmax(const Jet<T, N>& x, const T& y) { - return x < y ? Jet<T, N>{y} : x; + return fmax(x, Jet<T, N>{y}); } + template <typename T, int N> inline Jet<T, N> fmax(const T& x, const Jet<T, N>& y) { - return x < y ? y : Jet<T, N>{x}; + return fmax(Jet<T, N>{x}, y); } template <typename T, int N> inline Jet<T, N> fmin(const Jet<T, N>& x, const Jet<T, N>& y) { - return y < x ? y : x; + using std::isless; + return isnan(x.a) || isless(y.a, x.a) ? y : x; } + template <typename T, int N> inline Jet<T, N> fmin(const Jet<T, N>& x, const T& y) { - return y < x ? Jet<T, N>{y} : x; + return fmin(x, Jet<T, N>{y}); } + template <typename T, int N> inline Jet<T, N> fmin(const T& x, const Jet<T, N>& y) { - return y < x ? y : Jet<T, N>{x}; + return fmin(Jet<T, N>{x}, y); } // erf is defined as an integral that cannot be expressed analytically
diff --git a/internal/ceres/jet_test.cc b/internal/ceres/jet_test.cc index bceb9e4..32b1361 100644 --- a/internal/ceres/jet_test.cc +++ b/internal/ceres/jet_test.cc
@@ -834,6 +834,16 @@ VL << "z = " << z; ExpectJetsClose(x, z); } + { + J z = fmax(std::numeric_limits<double>::quiet_NaN(), x); + VL << "z = " << z; + ExpectJetsClose(x, z); + } + { + J z = fmax(x, std::numeric_limits<double>::quiet_NaN()); + VL << "z = " << z; + ExpectJetsClose(x, z); + } { J z = fmin(x, y); @@ -865,6 +875,17 @@ VL << "z = " << z; ExpectJetsClose(J{y.a}, z); } + { + J z = fmin(x, std::numeric_limits<double>::quiet_NaN()); + VL << "z = " << z; + ExpectJetsClose(x, z); + } + { + J z = fmin(std::numeric_limits<double>::quiet_NaN(), x); + VL << "z = " << z; + ExpectJetsClose(x, z); + } + { // copysign(x, +1) J z = copysign(x, J{+1}); VL << "z = " << z;