Add IsNaN operator! (#603)

* Add IsNaN operator!

* Docs

* More concise impl
diff --git a/doc/markdown/assertions.md b/doc/markdown/assertions.md
index 381dd52..5a6ad5c 100644
--- a/doc/markdown/assertions.md
+++ b/doc/markdown/assertions.md
@@ -163,6 +163,8 @@
 CHECK(doctest::IsNaN(performComputation()); // captures the result!
 ```
 
+`IsNaN` is able to capture the value, even if negated via `!`.
+
 --------
 
 - Check out the [**example**](../../examples/all_features/assertion_macros.cpp) which shows many of these macros
diff --git a/doctest/doctest.h b/doctest/doctest.h
index 6c292a1..988a7e0 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -1104,8 +1104,9 @@
 template <typename F>
 struct DOCTEST_INTERFACE_DECL IsNaN
 {
-    F val;
-    IsNaN(F f) : val(f) { }
+    F value; bool flipped;
+    IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+    IsNaN<F> operator!() const { return { value, !flipped }; }
     operator bool() const;
 };
 #ifndef __MINGW32__
@@ -3645,6 +3646,12 @@
 namespace detail {
     template <typename T>
     String fpToString(T value, int precision) {
+        if (std::isnan(value)) {
+            return "nan";
+        } else if (std::isinf(value)) {
+            return value > 0 ? "inf" : "-inf";
+        }
+
         std::ostringstream oss;
         oss << std::setprecision(precision) << std::fixed << value;
         std::string d = oss.str();
@@ -3727,14 +3734,14 @@
 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
 template <typename F>
 IsNaN<F>::operator bool() const {
-    return std::isnan(val);
+    return std::isnan(value) ^ flipped;
 }
 DOCTEST_MSVC_SUPPRESS_WARNING_POP
 template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
 template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
 template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
 template <typename F>
-String toString(IsNaN<F> nanCheck) { return "IsNaN( " + doctest::toString(nanCheck.val) + " )"; }
+String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; }
 String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); }
 String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); }
 String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); }
diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp
index 81e90ca..ec9db19 100644
--- a/doctest/parts/doctest.cpp
+++ b/doctest/parts/doctest.cpp
@@ -759,6 +759,12 @@
 namespace detail {
     template <typename T>
     String fpToString(T value, int precision) {
+        if (std::isnan(value)) {
+            return "nan";
+        } else if (std::isinf(value)) {
+            return value > 0 ? "inf" : "-inf";
+        }
+
         std::ostringstream oss;
         oss << std::setprecision(precision) << std::fixed << value;
         std::string d = oss.str();
@@ -841,14 +847,14 @@
 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
 template <typename F>
 IsNaN<F>::operator bool() const {
-    return std::isnan(val);
+    return std::isnan(value) ^ flipped;
 }
 DOCTEST_MSVC_SUPPRESS_WARNING_POP
 template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
 template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
 template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
 template <typename F>
-String toString(IsNaN<F> nanCheck) { return "IsNaN( " + doctest::toString(nanCheck.val) + " )"; }
+String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; }
 String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); }
 String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); }
 String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); }
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index aa789af..ae18209 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -1101,8 +1101,9 @@
 template <typename F>
 struct DOCTEST_INTERFACE_DECL IsNaN
 {
-    F val;
-    IsNaN(F f) : val(f) { }
+    F value; bool flipped;
+    IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+    IsNaN<F> operator!() const { return { value, !flipped }; }
     operator bool() const;
 };
 #ifndef __MINGW32__
diff --git a/examples/all_features/stringification.cpp b/examples/all_features/stringification.cpp
index 69f89bb..626e486 100644
--- a/examples/all_features/stringification.cpp
+++ b/examples/all_features/stringification.cpp
@@ -179,7 +179,7 @@
 
     CHECK(doctest::IsNaN<double>(0.5));
     CHECK(doctest::IsNaN<float>(std::numeric_limits<float>::infinity()));
-    // can't test actual nan because it's implementation defined
+    CHECK(!doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN()));
 
     // lets see if this exception gets translated
     throw_if(true, bla1);
diff --git a/examples/all_features/test_output/stringification.cpp.txt b/examples/all_features/test_output/stringification.cpp.txt
index a6c43dd..fbe1440 100644
--- a/examples/all_features/test_output/stringification.cpp.txt
+++ b/examples/all_features/test_output/stringification.cpp.txt
@@ -70,6 +70,9 @@
 stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct!
   values: CHECK( IsNaN( inff ) )
 
+stringification.cpp(0): ERROR: CHECK( !doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN()) ) is NOT correct!
+  values: CHECK( ! IsNaN( nanL ) )
+
 stringification.cpp(0): ERROR: test case THREW exception: MyTypeInherited<int>(5, 4)
 
 ===============================================================================
@@ -80,6 +83,6 @@
 
 ===============================================================================
 [doctest] test cases:  3 | 0 passed |  3 failed |
-[doctest] assertions: 16 | 2 passed | 14 failed |
+[doctest] assertions: 17 | 2 passed | 15 failed |
 [doctest] Status: FAILURE!
 Program code.
diff --git a/examples/all_features/test_output/stringification.cpp_junit.txt b/examples/all_features/test_output/stringification.cpp_junit.txt
index 6ed2e17..2ef89ce 100644
--- a/examples/all_features/test_output/stringification.cpp_junit.txt
+++ b/examples/all_features/test_output/stringification.cpp_junit.txt
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <testsuites>
-  <testsuite name="all_features" errors="2" failures="14" tests="16">
+  <testsuite name="all_features" errors="2" failures="15" tests="17">
     <testcase classname="stringification.cpp" name="no headers" status="run">
       <failure message="1as == nullptr" type="CHECK">
 stringification.cpp(0):
@@ -91,6 +91,12 @@
   values: CHECK( IsNaN( inff ) )
 
       </failure>
+      <failure message="! IsNaN( nanL )" type="CHECK">
+stringification.cpp(0):
+CHECK( !doctest::IsNaN&lt;double long>(std::numeric_limits&lt;double long>::quiet_NaN()) ) is NOT correct!
+  values: CHECK( ! IsNaN( nanL ) )
+
+      </failure>
       <error message="exception">
         MyTypeInherited&lt;int>(5, 4)
       </error>
diff --git a/examples/all_features/test_output/stringification.cpp_xml.txt b/examples/all_features/test_output/stringification.cpp_xml.txt
index de58856..6313afe 100644
--- a/examples/all_features/test_output/stringification.cpp_xml.txt
+++ b/examples/all_features/test_output/stringification.cpp_xml.txt
@@ -172,10 +172,18 @@
           IsNaN( inff )
         </Expanded>
       </Expression>
+      <Expression success="false" type="CHECK" filename="stringification.cpp" line="0">
+        <Original>
+          !doctest::IsNaN&lt;double long>(std::numeric_limits&lt;double long>::quiet_NaN())
+        </Original>
+        <Expanded>
+          ! IsNaN( nanL )
+        </Expanded>
+      </Expression>
       <Exception crash="false">
         MyTypeInherited&lt;int>(5, 4)
       </Exception>
-      <OverallResultsAsserts successes="0" failures="9" test_case_success="false"/>
+      <OverallResultsAsserts successes="0" failures="10" test_case_success="false"/>
     </TestCase>
     <TestCase name="a test case that registers an exception translator for int and then throws one" filename="stringification.cpp" line="0">
       <Exception crash="false">
@@ -184,7 +192,7 @@
       <OverallResultsAsserts successes="0" failures="0" test_case_success="false"/>
     </TestCase>
   </TestSuite>
-  <OverallResultsAsserts successes="2" failures="14"/>
+  <OverallResultsAsserts successes="2" failures="15"/>
   <OverallResultsTestCases successes="0" failures="3"/>
 </doctest>
 Program code.