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;