Nan check (#582)

* CHECK_NAN (Resolves #105)

* Fix helper macro

* Include correct test header

* Move definitions to cpp file

* Extern declaration and exception fix

* Fix more warnings

* Warning suppression

* Add NaN checking docs

* Improved wording
diff --git a/doc/markdown/assertions.md b/doc/markdown/assertions.md
index bdd33af..6c8e29a 100644
--- a/doc/markdown/assertions.md
+++ b/doc/markdown/assertions.md
@@ -133,6 +133,17 @@
 
 Currently [**logging macros**](logging.md) cannot be used for extra context for asserts outside of a test run. That means that the ```_MESSAGE``` variants of asserts are also not usable - since they are just a packed ```INFO()``` with an assert right after it.
 
+## NaN checking
+
+```<LEVEL>``` is one of 3 possible: ```REQUIRE```/```CHECK```/```WARN```.
+
+- ```<LEVEL>_NAN(expression)```
+- ```<LEVEL>_NOT_NAN(expression)```
+
+These utility macros check if a floating point value is or is not NaN respectively.
+
+They capture the actual float value on assertion failure.
+
 ## Floating point comparisons
 
 When comparing floating point numbers - especially if at least one of them has been computed - great care must be taken to allow for rounding errors and inexact representations.
diff --git a/doctest/doctest.h b/doctest/doctest.h
index d25f526..da01f1c 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -231,7 +231,8 @@
     DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */           \
     DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
     DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */                   \
-    DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */
+    DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */     \
+    DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */
 
 #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
 
@@ -444,6 +445,7 @@
 #ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
 #define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
 #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#include <cmath>
 #include <cstddef>
 #include <ostream>
 #include <istream>
@@ -631,6 +633,8 @@
         is_ge = 2 * is_gt,
         is_le = 2 * is_ge,
 
+        is_nan = 2 * is_le,
+
         // macro types
 
         DT_WARN    = is_normal | is_warn,
@@ -692,6 +696,14 @@
         DT_WARN_UNARY_FALSE    = is_normal | is_false | is_unary | is_warn,
         DT_CHECK_UNARY_FALSE   = is_normal | is_false | is_unary | is_check,
         DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require,
+
+        DT_WARN_NAN = is_normal | is_nan | is_warn,
+        DT_CHECK_NAN = is_normal | is_nan | is_check,
+        DT_REQUIRE_NAN = is_normal | is_nan | is_require,
+
+        DT_WARN_NOT_NAN = is_normal | is_nan | is_false | is_warn,
+        DT_CHECK_NOT_NAN = is_normal | is_nan | is_false | is_check,
+        DT_REQUIRE_NOT_NAN = is_normal | is_nan | is_false | is_require,
     };
 } // namespace assertType
 
@@ -1489,6 +1501,12 @@
     DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
     DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
 
+    template <typename T>
+    bool is_nan(T);
+    extern template bool is_nan(float);
+    extern template bool is_nan(double);
+    extern template bool is_nan(long double);
+
     struct DOCTEST_INTERFACE ResultBuilder : public AssertData
     {
         ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
@@ -1518,6 +1536,19 @@
             return !m_failed;
         }
 
+        template <typename L>
+        DOCTEST_NOINLINE bool nan_assert(L val) {
+            m_failed = !is_nan(val);
+
+            if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+                m_failed = !m_failed;
+
+            if(m_failed || getContextOptions()->success)
+                m_decomp = toString(val);
+
+            return !m_failed;
+        }
+
         void translateException();
 
         bool log();
@@ -1597,6 +1628,23 @@
         return !failed;
     }
 
+    template <typename L>
+    DOCTEST_NOINLINE bool nan_assert(assertType::Enum at, const char* file, int line,
+                                     const char* expr, L val) {
+        bool failed = !is_nan(val);
+
+        if (at & assertType::is_false) //!OCLINT bitwise operator in conditional
+            failed = !failed;
+
+        // ###################################################################################
+        // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+        // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+        // ###################################################################################
+        DOCTEST_ASSERT_OUT_OF_TESTS(toString(val));
+        DOCTEST_ASSERT_IN_TESTS(toString(val));
+        return !failed;
+    }
+
     struct DOCTEST_INTERFACE IExceptionTranslator
     {
         IExceptionTranslator();
@@ -2382,6 +2430,32 @@
 #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
 
+#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_NAN_ASSERT(assert_type, ...)                                                       \
+    [&] {                                                                                          \
+        doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__,      \
+                                                   __LINE__, #__VA_ARGS__);                        \
+        DOCTEST_WRAP_IN_TRY(                                                                       \
+            DOCTEST_RB.nan_assert(__VA_ARGS__))                                                    \
+        DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB);                                               \
+    }()
+
+#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_NAN_ASSERT(assert_type, ...)                                            \
+    doctest::detail::nan_assert(doctest::assertType::assert_type, __FILE__, __LINE__,   \
+        #__VA_ARGS__, __VA_ARGS__)
+
+#endif
+
+#define DOCTEST_WARN_NAN(...) DOCTEST_NAN_ASSERT(DT_WARN_NAN, __VA_ARGS__)
+#define DOCTEST_CHECK_NAN(...) DOCTEST_NAN_ASSERT(DT_CHECK_NAN, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NAN(...) DOCTEST_NAN_ASSERT(DT_REQUIRE_NAN, __VA_ARGS__)
+#define DOCTEST_WARN_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_WARN_NOT_NAN, __VA_ARGS__)
+#define DOCTEST_CHECK_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_CHECK_NOT_NAN, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_REQUIRE_NOT_NAN, __VA_ARGS__)
+
 #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
 
 #undef DOCTEST_WARN_THROWS
@@ -2600,6 +2674,12 @@
 #define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
 #define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_WARN_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_WARN_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
 
 #else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
 
@@ -2643,6 +2723,13 @@
 #define DOCTEST_CHECK_UNARY_FALSE(...) ([] { return false; })
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) ([] { return false; })
 
+#define DOCTEST_WARN_NAN(...) [&] { return false; }()
+#define DOCTEST_CHECK_NAN(...) [&] { return false; }()
+#define DOCTEST_REQUIRE_NAN(...) [&] { return false; }()
+#define DOCTEST_WARN_NOT_NAN(...) [&] { return false; }()
+#define DOCTEST_CHECK_NOT_NAN(...) [&] { return false; }()
+#define DOCTEST_REQUIRE_NOT_NAN(...) [&] { return false; }()
+
 #endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
 
 // TODO: think about if these also need to work properly even when doctest is disabled
@@ -2832,6 +2919,13 @@
 #define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__)
 #define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
 
+#define WARN_NAN(...) DOCTEST_WARN_NAN(__VA_ARGS__)
+#define CHECK_NAN(...) DOCTEST_CHECK_NAN(__VA_ARGS__)
+#define REQUIRE_NAN(...) DOCTEST_REQUIRE_NAN(__VA_ARGS__)
+#define WARN_NOT_NAN(...) DOCTEST_WARN_NOT_NAN(__VA_ARGS__)
+#define CHECK_NOT_NAN(...) DOCTEST_CHECK_NOT_NAN(__VA_ARGS__)
+#define REQUIRE_NOT_NAN(...) DOCTEST_REQUIRE_NOT_NAN(__VA_ARGS__)
+
 // KEPT FOR BACKWARDS COMPATIBILITY
 #define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__)
 #define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__)
@@ -3603,64 +3697,45 @@
 
 // clang-format off
 const char* assertString(assertType::Enum at) {
-    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled
-    switch(at) {  //!OCLINT missing default in switch statements
-        case assertType::DT_WARN                    : return "WARN";
-        case assertType::DT_CHECK                   : return "CHECK";
-        case assertType::DT_REQUIRE                 : return "REQUIRE";
+    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitely handled
+    #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
+    #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type)
+    switch(at) {
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE);
 
-        case assertType::DT_WARN_FALSE              : return "WARN_FALSE";
-        case assertType::DT_CHECK_FALSE             : return "CHECK_FALSE";
-        case assertType::DT_REQUIRE_FALSE           : return "REQUIRE_FALSE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE);
 
-        case assertType::DT_WARN_THROWS             : return "WARN_THROWS";
-        case assertType::DT_CHECK_THROWS            : return "CHECK_THROWS";
-        case assertType::DT_REQUIRE_THROWS          : return "REQUIRE_THROWS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS);
 
-        case assertType::DT_WARN_THROWS_AS          : return "WARN_THROWS_AS";
-        case assertType::DT_CHECK_THROWS_AS         : return "CHECK_THROWS_AS";
-        case assertType::DT_REQUIRE_THROWS_AS       : return "REQUIRE_THROWS_AS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS);
 
-        case assertType::DT_WARN_THROWS_WITH        : return "WARN_THROWS_WITH";
-        case assertType::DT_CHECK_THROWS_WITH       : return "CHECK_THROWS_WITH";
-        case assertType::DT_REQUIRE_THROWS_WITH     : return "REQUIRE_THROWS_WITH";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH);
 
-        case assertType::DT_WARN_THROWS_WITH_AS     : return "WARN_THROWS_WITH_AS";
-        case assertType::DT_CHECK_THROWS_WITH_AS    : return "CHECK_THROWS_WITH_AS";
-        case assertType::DT_REQUIRE_THROWS_WITH_AS  : return "REQUIRE_THROWS_WITH_AS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS);
 
-        case assertType::DT_WARN_NOTHROW            : return "WARN_NOTHROW";
-        case assertType::DT_CHECK_NOTHROW           : return "CHECK_NOTHROW";
-        case assertType::DT_REQUIRE_NOTHROW         : return "REQUIRE_NOTHROW";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW);
 
-        case assertType::DT_WARN_EQ                 : return "WARN_EQ";
-        case assertType::DT_CHECK_EQ                : return "CHECK_EQ";
-        case assertType::DT_REQUIRE_EQ              : return "REQUIRE_EQ";
-        case assertType::DT_WARN_NE                 : return "WARN_NE";
-        case assertType::DT_CHECK_NE                : return "CHECK_NE";
-        case assertType::DT_REQUIRE_NE              : return "REQUIRE_NE";
-        case assertType::DT_WARN_GT                 : return "WARN_GT";
-        case assertType::DT_CHECK_GT                : return "CHECK_GT";
-        case assertType::DT_REQUIRE_GT              : return "REQUIRE_GT";
-        case assertType::DT_WARN_LT                 : return "WARN_LT";
-        case assertType::DT_CHECK_LT                : return "CHECK_LT";
-        case assertType::DT_REQUIRE_LT              : return "REQUIRE_LT";
-        case assertType::DT_WARN_GE                 : return "WARN_GE";
-        case assertType::DT_CHECK_GE                : return "CHECK_GE";
-        case assertType::DT_REQUIRE_GE              : return "REQUIRE_GE";
-        case assertType::DT_WARN_LE                 : return "WARN_LE";
-        case assertType::DT_CHECK_LE                : return "CHECK_LE";
-        case assertType::DT_REQUIRE_LE              : return "REQUIRE_LE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE);
 
-        case assertType::DT_WARN_UNARY              : return "WARN_UNARY";
-        case assertType::DT_CHECK_UNARY             : return "CHECK_UNARY";
-        case assertType::DT_REQUIRE_UNARY           : return "REQUIRE_UNARY";
-        case assertType::DT_WARN_UNARY_FALSE        : return "WARN_UNARY_FALSE";
-        case assertType::DT_CHECK_UNARY_FALSE       : return "CHECK_UNARY_FALSE";
-        case assertType::DT_REQUIRE_UNARY_FALSE     : return "REQUIRE_UNARY_FALSE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE);
+
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NAN);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOT_NAN);
+
+        default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!");
     }
     DOCTEST_MSVC_SUPPRESS_WARNING_POP
-    return "";
 }
 // clang-format on
 
@@ -4650,6 +4725,16 @@
 } // namespace
 namespace detail {
 
+    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
+    template <typename T>
+    bool is_nan(T t) {
+        return std::isnan(t);
+    }
+    DOCTEST_MSVC_SUPPRESS_WARNING_POP
+    template bool is_nan(float);
+    template bool is_nan(double);
+    template bool is_nan(long double);
+
     ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
                                  const char* exception_type, const char* exception_string) {
         m_test_case        = g_cs->currentTest;
diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp
index 77dd994..3cf867f 100644
--- a/doctest/parts/doctest.cpp
+++ b/doctest/parts/doctest.cpp
@@ -697,64 +697,45 @@
 
 // clang-format off
 const char* assertString(assertType::Enum at) {
-    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled
-    switch(at) {  //!OCLINT missing default in switch statements
-        case assertType::DT_WARN                    : return "WARN";
-        case assertType::DT_CHECK                   : return "CHECK";
-        case assertType::DT_REQUIRE                 : return "REQUIRE";
+    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitely handled
+    #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
+    #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type)
+    switch(at) {
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE);
 
-        case assertType::DT_WARN_FALSE              : return "WARN_FALSE";
-        case assertType::DT_CHECK_FALSE             : return "CHECK_FALSE";
-        case assertType::DT_REQUIRE_FALSE           : return "REQUIRE_FALSE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE);
 
-        case assertType::DT_WARN_THROWS             : return "WARN_THROWS";
-        case assertType::DT_CHECK_THROWS            : return "CHECK_THROWS";
-        case assertType::DT_REQUIRE_THROWS          : return "REQUIRE_THROWS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS);
 
-        case assertType::DT_WARN_THROWS_AS          : return "WARN_THROWS_AS";
-        case assertType::DT_CHECK_THROWS_AS         : return "CHECK_THROWS_AS";
-        case assertType::DT_REQUIRE_THROWS_AS       : return "REQUIRE_THROWS_AS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS);
 
-        case assertType::DT_WARN_THROWS_WITH        : return "WARN_THROWS_WITH";
-        case assertType::DT_CHECK_THROWS_WITH       : return "CHECK_THROWS_WITH";
-        case assertType::DT_REQUIRE_THROWS_WITH     : return "REQUIRE_THROWS_WITH";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH);
 
-        case assertType::DT_WARN_THROWS_WITH_AS     : return "WARN_THROWS_WITH_AS";
-        case assertType::DT_CHECK_THROWS_WITH_AS    : return "CHECK_THROWS_WITH_AS";
-        case assertType::DT_REQUIRE_THROWS_WITH_AS  : return "REQUIRE_THROWS_WITH_AS";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS);
 
-        case assertType::DT_WARN_NOTHROW            : return "WARN_NOTHROW";
-        case assertType::DT_CHECK_NOTHROW           : return "CHECK_NOTHROW";
-        case assertType::DT_REQUIRE_NOTHROW         : return "REQUIRE_NOTHROW";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW);
 
-        case assertType::DT_WARN_EQ                 : return "WARN_EQ";
-        case assertType::DT_CHECK_EQ                : return "CHECK_EQ";
-        case assertType::DT_REQUIRE_EQ              : return "REQUIRE_EQ";
-        case assertType::DT_WARN_NE                 : return "WARN_NE";
-        case assertType::DT_CHECK_NE                : return "CHECK_NE";
-        case assertType::DT_REQUIRE_NE              : return "REQUIRE_NE";
-        case assertType::DT_WARN_GT                 : return "WARN_GT";
-        case assertType::DT_CHECK_GT                : return "CHECK_GT";
-        case assertType::DT_REQUIRE_GT              : return "REQUIRE_GT";
-        case assertType::DT_WARN_LT                 : return "WARN_LT";
-        case assertType::DT_CHECK_LT                : return "CHECK_LT";
-        case assertType::DT_REQUIRE_LT              : return "REQUIRE_LT";
-        case assertType::DT_WARN_GE                 : return "WARN_GE";
-        case assertType::DT_CHECK_GE                : return "CHECK_GE";
-        case assertType::DT_REQUIRE_GE              : return "REQUIRE_GE";
-        case assertType::DT_WARN_LE                 : return "WARN_LE";
-        case assertType::DT_CHECK_LE                : return "CHECK_LE";
-        case assertType::DT_REQUIRE_LE              : return "REQUIRE_LE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE);
 
-        case assertType::DT_WARN_UNARY              : return "WARN_UNARY";
-        case assertType::DT_CHECK_UNARY             : return "CHECK_UNARY";
-        case assertType::DT_REQUIRE_UNARY           : return "REQUIRE_UNARY";
-        case assertType::DT_WARN_UNARY_FALSE        : return "WARN_UNARY_FALSE";
-        case assertType::DT_CHECK_UNARY_FALSE       : return "CHECK_UNARY_FALSE";
-        case assertType::DT_REQUIRE_UNARY_FALSE     : return "REQUIRE_UNARY_FALSE";
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE);
+
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NAN);
+        DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOT_NAN);
+
+        default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!");
     }
     DOCTEST_MSVC_SUPPRESS_WARNING_POP
-    return "";
 }
 // clang-format on
 
@@ -1744,6 +1725,16 @@
 } // namespace
 namespace detail {
 
+    DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
+    template <typename T>
+    bool is_nan(T t) {
+        return std::isnan(t);
+    }
+    DOCTEST_MSVC_SUPPRESS_WARNING_POP
+    template bool is_nan(float);
+    template bool is_nan(double);
+    template bool is_nan(long double);
+
     ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
                                  const char* exception_type, const char* exception_string) {
         m_test_case        = g_cs->currentTest;
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index b0d786f..dfd0545 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -228,7 +228,8 @@
     DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */           \
     DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
     DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */                   \
-    DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */
+    DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */     \
+    DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */
 
 #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
 
@@ -441,6 +442,7 @@
 #ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
 #define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
 #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#include <cmath>
 #include <cstddef>
 #include <ostream>
 #include <istream>
@@ -628,6 +630,8 @@
         is_ge = 2 * is_gt,
         is_le = 2 * is_ge,
 
+        is_nan = 2 * is_le,
+
         // macro types
 
         DT_WARN    = is_normal | is_warn,
@@ -689,6 +693,14 @@
         DT_WARN_UNARY_FALSE    = is_normal | is_false | is_unary | is_warn,
         DT_CHECK_UNARY_FALSE   = is_normal | is_false | is_unary | is_check,
         DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require,
+
+        DT_WARN_NAN = is_normal | is_nan | is_warn,
+        DT_CHECK_NAN = is_normal | is_nan | is_check,
+        DT_REQUIRE_NAN = is_normal | is_nan | is_require,
+
+        DT_WARN_NOT_NAN = is_normal | is_nan | is_false | is_warn,
+        DT_CHECK_NOT_NAN = is_normal | is_nan | is_false | is_check,
+        DT_REQUIRE_NOT_NAN = is_normal | is_nan | is_false | is_require,
     };
 } // namespace assertType
 
@@ -1486,6 +1498,12 @@
     DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
     DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
 
+    template <typename T>
+    bool is_nan(T);
+    extern template bool is_nan(float);
+    extern template bool is_nan(double);
+    extern template bool is_nan(long double);
+
     struct DOCTEST_INTERFACE ResultBuilder : public AssertData
     {
         ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
@@ -1515,6 +1533,19 @@
             return !m_failed;
         }
 
+        template <typename L>
+        DOCTEST_NOINLINE bool nan_assert(L val) {
+            m_failed = !is_nan(val);
+
+            if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+                m_failed = !m_failed;
+
+            if(m_failed || getContextOptions()->success)
+                m_decomp = toString(val);
+
+            return !m_failed;
+        }
+
         void translateException();
 
         bool log();
@@ -1594,6 +1625,23 @@
         return !failed;
     }
 
+    template <typename L>
+    DOCTEST_NOINLINE bool nan_assert(assertType::Enum at, const char* file, int line,
+                                     const char* expr, L val) {
+        bool failed = !is_nan(val);
+
+        if (at & assertType::is_false) //!OCLINT bitwise operator in conditional
+            failed = !failed;
+
+        // ###################################################################################
+        // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+        // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+        // ###################################################################################
+        DOCTEST_ASSERT_OUT_OF_TESTS(toString(val));
+        DOCTEST_ASSERT_IN_TESTS(toString(val));
+        return !failed;
+    }
+
     struct DOCTEST_INTERFACE IExceptionTranslator
     {
         IExceptionTranslator();
@@ -2379,6 +2427,32 @@
 #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
 
+#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_NAN_ASSERT(assert_type, ...)                                                       \
+    [&] {                                                                                          \
+        doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__,      \
+                                                   __LINE__, #__VA_ARGS__);                        \
+        DOCTEST_WRAP_IN_TRY(                                                                       \
+            DOCTEST_RB.nan_assert(__VA_ARGS__))                                                    \
+        DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB);                                               \
+    }()
+
+#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_NAN_ASSERT(assert_type, ...)                                            \
+    doctest::detail::nan_assert(doctest::assertType::assert_type, __FILE__, __LINE__,   \
+        #__VA_ARGS__, __VA_ARGS__)
+
+#endif
+
+#define DOCTEST_WARN_NAN(...) DOCTEST_NAN_ASSERT(DT_WARN_NAN, __VA_ARGS__)
+#define DOCTEST_CHECK_NAN(...) DOCTEST_NAN_ASSERT(DT_CHECK_NAN, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NAN(...) DOCTEST_NAN_ASSERT(DT_REQUIRE_NAN, __VA_ARGS__)
+#define DOCTEST_WARN_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_WARN_NOT_NAN, __VA_ARGS__)
+#define DOCTEST_CHECK_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_CHECK_NOT_NAN, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NOT_NAN(...) DOCTEST_NAN_ASSERT(DT_REQUIRE_NOT_NAN, __VA_ARGS__)
+
 #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
 
 #undef DOCTEST_WARN_THROWS
@@ -2597,6 +2671,12 @@
 #define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
 #define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_WARN_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NAN(...) [&] { return doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_WARN_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NOT_NAN(...) [&] { return !doctest::detail::is_nan(__VA_ARGS__); }()
 
 #else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
 
@@ -2640,6 +2720,13 @@
 #define DOCTEST_CHECK_UNARY_FALSE(...) ([] { return false; })
 #define DOCTEST_REQUIRE_UNARY_FALSE(...) ([] { return false; })
 
+#define DOCTEST_WARN_NAN(...) [&] { return false; }()
+#define DOCTEST_CHECK_NAN(...) [&] { return false; }()
+#define DOCTEST_REQUIRE_NAN(...) [&] { return false; }()
+#define DOCTEST_WARN_NOT_NAN(...) [&] { return false; }()
+#define DOCTEST_CHECK_NOT_NAN(...) [&] { return false; }()
+#define DOCTEST_REQUIRE_NOT_NAN(...) [&] { return false; }()
+
 #endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
 
 // TODO: think about if these also need to work properly even when doctest is disabled
@@ -2829,6 +2916,13 @@
 #define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__)
 #define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
 
+#define WARN_NAN(...) DOCTEST_WARN_NAN(__VA_ARGS__)
+#define CHECK_NAN(...) DOCTEST_CHECK_NAN(__VA_ARGS__)
+#define REQUIRE_NAN(...) DOCTEST_REQUIRE_NAN(__VA_ARGS__)
+#define WARN_NOT_NAN(...) DOCTEST_WARN_NOT_NAN(__VA_ARGS__)
+#define CHECK_NOT_NAN(...) DOCTEST_CHECK_NOT_NAN(__VA_ARGS__)
+#define REQUIRE_NOT_NAN(...) DOCTEST_REQUIRE_NOT_NAN(__VA_ARGS__)
+
 // KEPT FOR BACKWARDS COMPATIBILITY
 #define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__)
 #define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__)
diff --git a/examples/all_features/assertion_macros.cpp b/examples/all_features/assertion_macros.cpp
index d2f0ada..8dcebb9 100644
--- a/examples/all_features/assertion_macros.cpp
+++ b/examples/all_features/assertion_macros.cpp
@@ -4,6 +4,8 @@
 
 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
 #include <stdexcept>
+#include <cmath>
+#include <limits>
 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
 
 TEST_CASE("normal macros") {
@@ -204,3 +206,11 @@
     if (CHECK_THROWS([]{}())) { MESSAGE("should not be reached!"); }
     DOCTEST_MSVC_SUPPRESS_WARNING_POP
 }
+
+TEST_CASE("nan") {
+    REQUIRE_NOT_NAN(0.f);
+    CHECK_NAN(std::numeric_limits<long double>::infinity());
+    CHECK_NOT_NAN(0.);
+    WARN_NOT_NAN(std::numeric_limits<float>::quiet_NaN());
+    REQUIRE_NAN(std::numeric_limits<long double>::signaling_NaN());
+}
diff --git a/examples/all_features/asserts_used_outside_of_tests.cpp b/examples/all_features/asserts_used_outside_of_tests.cpp
index 289c266..b427348 100644
--- a/examples/all_features/asserts_used_outside_of_tests.cpp
+++ b/examples/all_features/asserts_used_outside_of_tests.cpp
@@ -22,6 +22,7 @@
 
     CHECK(false);
     CHECK_THROWS(std::cout << "hello! \n");
+    CHECK_NAN(0.);
 }
 
 // std::mutex g_mut;
diff --git a/examples/all_features/test_output/assertion_macros.cpp.txt b/examples/all_features/test_output/assertion_macros.cpp.txt
index e377396..0366010 100644
--- a/examples/all_features/test_output/assertion_macros.cpp.txt
+++ b/examples/all_features/test_output/assertion_macros.cpp.txt
@@ -219,7 +219,17 @@
 assertion_macros.cpp(0): ERROR: CHECK_THROWS( []{}() ) did NOT throw at all!
 
 ===============================================================================
-[doctest] test cases: 22 |  3 passed | 19 failed |
-[doctest] assertions: 80 | 35 passed | 45 failed |
+assertion_macros.cpp(0):
+TEST CASE:  nan
+
+assertion_macros.cpp(0): ERROR: CHECK_NAN( std::numeric_limits<long double>::infinity() ) is NOT correct!
+  values: CHECK_NAN( inf )
+
+assertion_macros.cpp(0): WARNING: WARN_NOT_NAN( std::numeric_limits<float>::quiet_NaN() ) is NOT correct!
+  values: WARN_NOT_NAN( nanf )
+
+===============================================================================
+[doctest] test cases: 23 |  3 passed | 20 failed |
+[doctest] assertions: 84 | 38 passed | 46 failed |
 [doctest] Status: FAILURE!
 Program code.
diff --git a/examples/all_features/test_output/assertion_macros.cpp_junit.txt b/examples/all_features/test_output/assertion_macros.cpp_junit.txt
index d9a23f0..904841d 100644
--- a/examples/all_features/test_output/assertion_macros.cpp_junit.txt
+++ b/examples/all_features/test_output/assertion_macros.cpp_junit.txt
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <testsuites>
-  <testsuite name="all_features" errors="0" failures="58" tests="80">
+  <testsuite name="all_features" errors="0" failures="60" tests="84">
     <testcase classname="assertion_macros.cpp" name="normal macros" status="run">
       <failure type="CHECK">
 assertion_macros.cpp(0):
@@ -354,6 +354,20 @@
 
       </failure>
     </testcase>
+    <testcase classname="assertion_macros.cpp" name="nan" status="run">
+      <failure message="inf" type="CHECK_NAN">
+assertion_macros.cpp(0):
+CHECK_NAN( std::numeric_limits&lt;long double>::infinity() ) is NOT correct!
+  values: CHECK_NAN( inf )
+
+      </failure>
+      <failure message="nanf" type="WARN_NOT_NAN">
+assertion_macros.cpp(0):
+WARN_NOT_NAN( std::numeric_limits&lt;float>::quiet_NaN() ) is NOT correct!
+  values: WARN_NOT_NAN( nanf )
+
+      </failure>
+    </testcase>
   </testsuite>
 </testsuites>
 Program code.
diff --git a/examples/all_features/test_output/assertion_macros.cpp_xml.txt b/examples/all_features/test_output/assertion_macros.cpp_xml.txt
index 7b64f0d..65245d9 100644
--- a/examples/all_features/test_output/assertion_macros.cpp_xml.txt
+++ b/examples/all_features/test_output/assertion_macros.cpp_xml.txt
@@ -580,8 +580,27 @@
       </Expression>
       <OverallResultsAsserts successes="0" failures="6" test_case_success="false"/>
     </TestCase>
+    <TestCase name="nan" filename="assertion_macros.cpp" line="0">
+      <Expression success="false" type="CHECK_NAN" filename="assertion_macros.cpp" line="0">
+        <Original>
+          std::numeric_limits&lt;long double>::infinity()
+        </Original>
+        <Expanded>
+          inf
+        </Expanded>
+      </Expression>
+      <Expression success="false" type="WARN_NOT_NAN" filename="assertion_macros.cpp" line="0">
+        <Original>
+          std::numeric_limits&lt;float>::quiet_NaN()
+        </Original>
+        <Expanded>
+          nanf
+        </Expanded>
+      </Expression>
+      <OverallResultsAsserts successes="3" failures="1" test_case_success="false"/>
+    </TestCase>
   </TestSuite>
-  <OverallResultsAsserts successes="35" failures="45"/>
-  <OverallResultsTestCases successes="3" failures="19"/>
+  <OverallResultsAsserts successes="38" failures="46"/>
+  <OverallResultsTestCases successes="3" failures="20"/>
 </doctest>
 Program code.
diff --git a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt
index 9c09060..f2d8577 100644
--- a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt
+++ b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt
@@ -14,3 +14,5 @@
   values: CHECK( false )
 hello! 
 asserts_used_outside_of_tests.cpp(24): ERROR: an assert dealing with exceptions has failed!
+asserts_used_outside_of_tests.cpp(25): ERROR: CHECK_NAN( 0. ) is NOT correct!
+  values: CHECK_NAN( 0.0 )
diff --git a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_junit.txt b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_junit.txt
index 5727760..0b9472b 100644
--- a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_junit.txt
+++ b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_junit.txt
@@ -13,3 +13,5 @@
   values: CHECK( false )
 hello! 
 asserts_used_outside_of_tests.cpp(24): ERROR: an assert dealing with exceptions has failed!
+asserts_used_outside_of_tests.cpp(25): ERROR: CHECK_NAN( 0. ) is NOT correct!
+  values: CHECK_NAN( 0.0 )
diff --git a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_xml.txt b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_xml.txt
index 6490fb1..935fda8 100644
--- a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_xml.txt
+++ b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp_xml.txt
@@ -15,3 +15,5 @@
   values: CHECK( false )
 hello! 
 asserts_used_outside_of_tests.cpp(24): ERROR: an assert dealing with exceptions has failed!
+asserts_used_outside_of_tests.cpp(25): ERROR: CHECK_NAN( 0. ) is NOT correct!
+  values: CHECK_NAN( 0.0 )
diff --git a/examples/all_features/test_output/filter_2.txt b/examples/all_features/test_output/filter_2.txt
index d4b108f..662a7e2 100644
--- a/examples/all_features/test_output/filter_2.txt
+++ b/examples/all_features/test_output/filter_2.txt
@@ -1,6 +1,6 @@
 [doctest] run with "--help" for options
 ===============================================================================
-[doctest] test cases: 0 | 0 passed | 0 failed | 95 skipped
+[doctest] test cases: 0 | 0 passed | 0 failed | 96 skipped
 [doctest] assertions: 0 | 0 passed | 0 failed |
 [doctest] Status: SUCCESS!
 Program code.
diff --git a/examples/all_features/test_output/filter_2_xml.txt b/examples/all_features/test_output/filter_2_xml.txt
index 90a2824..57a62a0 100644
--- a/examples/all_features/test_output/filter_2_xml.txt
+++ b/examples/all_features/test_output/filter_2_xml.txt
@@ -88,6 +88,7 @@
     <TestCase name="namespace 7 member vs global" filename="namespace7.cpp" line="0" skipped="true"/>
     <TestCase name="namespace 8 friend vs global" filename="namespace8.cpp" line="0" skipped="true"/>
     <TestCase name="namespace 9 both global" filename="namespace9.cpp" line="0" skipped="true"/>
+    <TestCase name="nan" filename="assertion_macros.cpp" line="0" skipped="true"/>
     <TestCase name="no checks" filename="no_failures.cpp" line="0" skipped="true"/>
     <TestCase name="normal macros" filename="assertion_macros.cpp" line="0" skipped="true"/>
   </TestSuite>
@@ -135,6 +136,6 @@
     <TestCase name="will end from an unknown exception" filename="coverage_maxout.cpp" line="0" skipped="true"/>
   </TestSuite>
   <OverallResultsAsserts successes="0" failures="0"/>
-  <OverallResultsTestCases successes="0" failures="0" skipped="95"/>
+  <OverallResultsTestCases successes="0" failures="0" skipped="96"/>
 </doctest>
 Program code.
diff --git a/scripts/coverage_maxout.cpp b/scripts/coverage_maxout.cpp
index ed3b4fb..8b6e574 100644
--- a/scripts/coverage_maxout.cpp
+++ b/scripts/coverage_maxout.cpp
@@ -15,6 +15,7 @@
 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
 #include <ostream>
 #include <sstream>
+#include <stdexcept>
 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
 
 #ifndef DOCTEST_CONFIG_DISABLE
@@ -94,7 +95,11 @@
     // trigger code path for String to ostream through operator<<
     oss << str;
     // trigger code path for assert string of a non-existent assert type
-    oss << assertString(static_cast<assertType::Enum>(3));
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+    try {
+        assertString(static_cast<assertType::Enum>(3));
+    } catch (const std::logic_error&) { }
+#endif
     str += oss.str().c_str();
     str += failureString(assertType::is_normal);
     CHECK(str == "omgomgomgaaaNULLtrue00.5f0.50.199991111111true0.50.50.1cc"