pw_assert: adds float near and float exact

- Renames PW_{D,}CHECK_FLOAT_* macros to
  PW_{D,}CHECK_FLOAT_EXACT_* to make it really obvious that it
  ignores floating point precision limits and ergo accumulated
  error which may cause accidental Asserts if the developer
  was not aware of this.
- Adds PW_{D,}CHECK_FLOAT_NEAR to provide an Assert that allows
  the developer to take float precision limits into account.
- Updates some stale comments and TODOs.

Change-Id: Id39921775db6386bc48dc8a9fbc7afd9937addfb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/14041
Commit-Queue: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_assert/assert_backend_compile_test.cc b/pw_assert/assert_backend_compile_test.cc
index 372810b..da19e34 100644
--- a/pw_assert/assert_backend_compile_test.cc
+++ b/pw_assert/assert_backend_compile_test.cc
@@ -134,13 +134,14 @@
   float x_float = 50.5;
   float y_float = 66.5;
 
-  PW_CHECK_FLOAT_LE(x_float, y_float);
-  PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
-  PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
+  PW_CHECK_FLOAT_EXACT_LE(x_float, y_float);
+  PW_CHECK_FLOAT_EXACT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
+  PW_CHECK_FLOAT_EXACT_LE(
+      x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
 
-  PW_CHECK_FLOAT_GE(x_float, y_float);
-  PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
-  PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
+  PW_CHECK_FLOAT_EXACT_GE(x_float, y_float);
+  PW_CHECK_FLOAT_EXACT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
+  PW_CHECK_FLOAT_EXACT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
 }
 
 // Don't exhaustively test the DCHECKs but have a sampling of them.
@@ -150,7 +151,7 @@
   PW_DCHECK(5 == 10, "Message");
   PW_DCHECK(5 == 10, "Message: %d", 5);
   PW_DCHECK_INT_LE(5.4, 10.0);
-  PW_DCHECK_FLOAT_EQ(5.4, 10.0, "Message");
+  PW_DCHECK_FLOAT_EXACT_EQ(5.4, 10.0, "Message");
 }
 
 static int Add3(int a, int b, int c) { return a + b + c; }
diff --git a/pw_assert/assert_backend_compile_test_c.c b/pw_assert/assert_backend_compile_test_c.c
index fb81a94..cecc756 100644
--- a/pw_assert/assert_backend_compile_test_c.c
+++ b/pw_assert/assert_backend_compile_test_c.c
@@ -122,13 +122,14 @@
     float x_float = 50.5;
     float y_float = 66.5;
 
-    PW_CHECK_FLOAT_LE(x_float, y_float);
-    PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
-    PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
+    PW_CHECK_FLOAT_EXACT_LE(x_float, y_float);
+    PW_CHECK_FLOAT_EXACT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
+    PW_CHECK_FLOAT_EXACT_LE(
+        x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
 
-    PW_CHECK_FLOAT_GE(x_float, y_float);
-    PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
-    PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
+    PW_CHECK_FLOAT_EXACT_GE(x_float, y_float);
+    PW_CHECK_FLOAT_EXACT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
+    PW_CHECK_FLOAT_EXACT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
   }
 
   // Don't exhaustively test the DCHECKs but have a sampling of them.
@@ -138,7 +139,7 @@
     PW_DCHECK(5 == 10, "Message");
     PW_DCHECK(5 == 10, "Message: %d", 5);
     PW_DCHECK_INT_LE(5.4, 10.0);
-    PW_DCHECK_FLOAT_EQ(5.4, 10.0, "Message");
+    PW_DCHECK_FLOAT_EXACT_EQ(5.4, 10.0, "Message");
   }
 
   {  // TEST(Check, ComparisonArgumentsWithCommas)
diff --git a/pw_assert/assert_facade_test.cc b/pw_assert/assert_facade_test.cc
index a234562..fc48268 100644
--- a/pw_assert/assert_facade_test.cc
+++ b/pw_assert/assert_facade_test.cc
@@ -228,56 +228,99 @@
 TEST_F(AssertFail, PtrNotNull) { PW_CHECK_NOTNULL(0x0); }
 
 // PW_CHECK_FLOAT_*(...)
-// Binary checks with floats, comparisons: <, <=, =, !=, >, >=.
+// Binary checks with floats, comparisons: EXACT_LT, EXACT_LE, NEAR, EXACT_EQ,
+// EXACT_NE, EXACT_GE, EXACT_GT.
 
 // Test message formatting separate from the triggering.
 // Only test formatting for the type once.
 TEST_F(AssertFail, FloatLessThanNoMessageNoArguments) {
-  PW_CHECK_FLOAT_LT(5.2, 2.3);
+  PW_CHECK_FLOAT_EXACT_LT(5.2, 2.3);
   EXPECT_MESSAGE("Check failed: 5.2 (=5.200000) < 2.3 (=2.300000). ");
 }
 TEST_F(AssertFail, FloatLessThanMessageNoArguments) {
-  PW_CHECK_FLOAT_LT(5.2, 2.3, "msg");
+  PW_CHECK_FLOAT_EXACT_LT(5.2, 2.3, "msg");
   EXPECT_MESSAGE("Check failed: 5.2 (=5.200000) < 2.3 (=2.300000). msg");
 }
 TEST_F(AssertFail, FloatLessThanMessageArguments) {
-  PW_CHECK_FLOAT_LT(5.2, 2.3, "msg: %d", 6);
+  PW_CHECK_FLOAT_EXACT_LT(5.2, 2.3, "msg: %d", 6);
   EXPECT_MESSAGE("Check failed: 5.2 (=5.200000) < 2.3 (=2.300000). msg: 6");
 }
-
+// Check float NEAR both above and below the permitted range.
+TEST_F(AssertFail, FloatNearAboveNoMessageNoArguments) {
+  PW_CHECK_FLOAT_NEAR(5.2, 2.3, 0.1);
+  EXPECT_MESSAGE(
+      "Check failed: 5.2 (=5.200000) <= 2.3 + abs_tolerance (=2.400000). ");
+}
+TEST_F(AssertFail, FloatNearAboveMessageNoArguments) {
+  PW_CHECK_FLOAT_NEAR(5.2, 2.3, 0.1, "msg");
+  EXPECT_MESSAGE(
+      "Check failed: 5.2 (=5.200000) <= 2.3 + abs_tolerance (=2.400000). msg");
+}
+TEST_F(AssertFail, FloatNearAboveMessageArguments) {
+  PW_CHECK_FLOAT_NEAR(5.2, 2.3, 0.1, "msg: %d", 6);
+  EXPECT_MESSAGE(
+      "Check failed: 5.2 (=5.200000) <= 2.3 + abs_tolerance (=2.400000). msg: "
+      "6");
+}
+TEST_F(AssertFail, FloatNearBelowNoMessageNoArguments) {
+  PW_CHECK_FLOAT_NEAR(1.2, 2.3, 0.1);
+  EXPECT_MESSAGE(
+      "Check failed: 1.2 (=1.200000) >= 2.3 - abs_tolerance (=2.200000). ");
+}
+TEST_F(AssertFail, FloatNearBelowMessageNoArguments) {
+  PW_CHECK_FLOAT_NEAR(1.2, 2.3, 0.1, "msg");
+  EXPECT_MESSAGE(
+      "Check failed: 1.2 (=1.200000) >= 2.3 - abs_tolerance (=2.200000). msg");
+}
+TEST_F(AssertFail, FloatNearBelowMessageArguments) {
+  PW_CHECK_FLOAT_NEAR(1.2, 2.3, 0.1, "msg: %d", 6);
+  EXPECT_MESSAGE(
+      "Check failed: 1.2 (=1.200000) >= 2.3 - abs_tolerance (=2.200000). msg: "
+      "6");
+}
 // Test comparison boundaries.
 // Note: The below example numbers all round to integer 1, to detect accidental
 // integer conversions in the asserts.
 
 // FLOAT <
-TEST_F(AssertPass, FloatLt1) { PW_CHECK_FLOAT_LT(1.1, 1.2); }
-TEST_F(AssertFail, FloatLt2) { PW_CHECK_FLOAT_LT(1.2, 1.2); }
-TEST_F(AssertFail, FloatLt3) { PW_CHECK_FLOAT_LT(1.2, 1.1); }
+TEST_F(AssertPass, FloatLt1) { PW_CHECK_FLOAT_EXACT_LT(1.1, 1.2); }
+TEST_F(AssertFail, FloatLt2) { PW_CHECK_FLOAT_EXACT_LT(1.2, 1.2); }
+TEST_F(AssertFail, FloatLt3) { PW_CHECK_FLOAT_EXACT_LT(1.2, 1.1); }
 
 // FLOAT <=
-TEST_F(AssertPass, FloatLe1) { PW_CHECK_FLOAT_LE(1.1, 1.2); }
-TEST_F(AssertPass, FloatLe2) { PW_CHECK_FLOAT_LE(1.2, 1.2); }
-TEST_F(AssertFail, FloatLe3) { PW_CHECK_FLOAT_LE(1.2, 1.1); }
+TEST_F(AssertPass, FloatLe1) { PW_CHECK_FLOAT_EXACT_LE(1.1, 1.2); }
+TEST_F(AssertPass, FloatLe2) { PW_CHECK_FLOAT_EXACT_LE(1.2, 1.2); }
+TEST_F(AssertFail, FloatLe3) { PW_CHECK_FLOAT_EXACT_LE(1.2, 1.1); }
+
+// FLOAT ~= based on absolute error.
+TEST_F(AssertFail, FloatNearAbs1) { PW_CHECK_FLOAT_NEAR(1.09, 1.2, 0.1); }
+TEST_F(AssertPass, FloatNearAbs2) { PW_CHECK_FLOAT_NEAR(1.1, 1.2, 0.1); }
+TEST_F(AssertPass, FloatNearAbs3) { PW_CHECK_FLOAT_NEAR(1.2, 1.2, 0.1); }
+TEST_F(AssertPass, FloatNearAbs4) { PW_CHECK_FLOAT_NEAR(1.2, 1.1, 0.1); }
+TEST_F(AssertFail, FloatNearAbs5) { PW_CHECK_FLOAT_NEAR(1.21, 1.1, 0.1); }
+// Make sure the abs_tolerance is asserted to be >= 0.
+TEST_F(AssertFail, FloatNearAbs6) { PW_CHECK_FLOAT_NEAR(1.2, 1.2, -0.1); }
+TEST_F(AssertPass, FloatNearAbs7) { PW_CHECK_FLOAT_NEAR(1.2, 1.2, 0.0); }
 
 // FLOAT ==
-TEST_F(AssertFail, FloatEq1) { PW_CHECK_FLOAT_EQ(1.1, 1.2); }
-TEST_F(AssertPass, FloatEq2) { PW_CHECK_FLOAT_EQ(1.2, 1.2); }
-TEST_F(AssertFail, FloatEq3) { PW_CHECK_FLOAT_EQ(1.2, 1.1); }
+TEST_F(AssertFail, FloatEq1) { PW_CHECK_FLOAT_EXACT_EQ(1.1, 1.2); }
+TEST_F(AssertPass, FloatEq2) { PW_CHECK_FLOAT_EXACT_EQ(1.2, 1.2); }
+TEST_F(AssertFail, FloatEq3) { PW_CHECK_FLOAT_EXACT_EQ(1.2, 1.1); }
 
 // FLOAT !=
-TEST_F(AssertPass, FloatNe1) { PW_CHECK_FLOAT_NE(1.1, 1.2); }
-TEST_F(AssertFail, FloatNe2) { PW_CHECK_FLOAT_NE(1.2, 1.2); }
-TEST_F(AssertPass, FloatNe3) { PW_CHECK_FLOAT_NE(1.2, 1.1); }
+TEST_F(AssertPass, FloatNe1) { PW_CHECK_FLOAT_EXACT_NE(1.1, 1.2); }
+TEST_F(AssertFail, FloatNe2) { PW_CHECK_FLOAT_EXACT_NE(1.2, 1.2); }
+TEST_F(AssertPass, FloatNe3) { PW_CHECK_FLOAT_EXACT_NE(1.2, 1.1); }
 
 // FLOAT >
-TEST_F(AssertFail, FloatGt1) { PW_CHECK_FLOAT_GT(1.1, 1.2); }
-TEST_F(AssertFail, FloatGt2) { PW_CHECK_FLOAT_GT(1.2, 1.2); }
-TEST_F(AssertPass, FloatGt3) { PW_CHECK_FLOAT_GT(1.2, 1.1); }
+TEST_F(AssertFail, FloatGt1) { PW_CHECK_FLOAT_EXACT_GT(1.1, 1.2); }
+TEST_F(AssertFail, FloatGt2) { PW_CHECK_FLOAT_EXACT_GT(1.2, 1.2); }
+TEST_F(AssertPass, FloatGt3) { PW_CHECK_FLOAT_EXACT_GT(1.2, 1.1); }
 
 // FLOAT >=
-TEST_F(AssertFail, FloatGe1) { PW_CHECK_FLOAT_GE(1.1, 1.2); }
-TEST_F(AssertPass, FloatGe2) { PW_CHECK_FLOAT_GE(1.2, 1.2); }
-TEST_F(AssertPass, FloatGe3) { PW_CHECK_FLOAT_GE(1.2, 1.1); }
+TEST_F(AssertFail, FloatGe1) { PW_CHECK_FLOAT_EXACT_GE(1.1, 1.2); }
+TEST_F(AssertPass, FloatGe2) { PW_CHECK_FLOAT_EXACT_GE(1.2, 1.2); }
+TEST_F(AssertPass, FloatGe3) { PW_CHECK_FLOAT_EXACT_GE(1.2, 1.1); }
 
 // Nested comma handling.
 static int Add3(int a, int b, int c) { return a + b + c; }
diff --git a/pw_assert/docs.rst b/pw_assert/docs.rst
index b3c6452..eb061b8 100644
--- a/pw_assert/docs.rst
+++ b/pw_assert/docs.rst
@@ -111,8 +111,8 @@
 Why isn't there a ``PW_CHECK_LE``? Why is the type (e.g. ``INT``) needed?
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 The problem with asserts like ``PW_CHECK_LE(a, b)`` instead of
-``PW_CHECK_INT_LE(a, b)`` or ``PW_CHECK_FLOAT_LE(a, b)`` is that to capture the
-arguments with the tokenizer, we need to know the types. Using the
+``PW_CHECK_INT_LE(a, b)`` or ``PW_CHECK_FLOAT_EXACT_LE(a, b)`` is that to
+capture the arguments with the tokenizer, we need to know the types. Using the
 preprocessor, it is impossible to dispatch based on the types of ``a`` and
 ``b``, so unfortunately having a separate macro for each of the types commonly
 asserted on is necessary.
@@ -193,7 +193,8 @@
     +------------------------------------+-------------------------------------+
     | ``PW_CHECK(a_ptr <= b_ptr)``       | ``PW_CHECK_PTR_LE(a_ptr, b_ptr)``   |
     +------------------------------------+-------------------------------------+
-    | ``PW_CHECK(Temp() <= 10.0)``       | ``PW_CHECK_FLOAT_LE(Temp(), 10.0)`` |
+    | ``PW_CHECK(Temp() <= 10.0)``       | ``PW_CHECK_FLOAT_EXACT_LE(``        |
+    |                                    | ``    Temp(), 10.0)``               |
     +------------------------------------+-------------------------------------+
     | ``PW_CHECK(Foo() == Status::OK)``  | ``PW_CHECK_OK(Foo())``              |
     +------------------------------------+-------------------------------------+
@@ -242,118 +243,156 @@
 
   .. code-block:: cpp
 
-    PW_CHECK_FLOAT_GE(BatteryVoltage(), 3.2, "System state=%s", SysState());
+    PW_CHECK_FLOAT_EXACT_GE(BatteryVoltage(), 3.2,
+                            "System state=%s", SysState());
 
   Below is the full list of binary comparison assert macros, along with the
   type specifier. The specifier is irrelevant to application authors but is
   needed for backend implementers.
 
-  +-------------------+--------------+-----------+-----------------------+
-  | Macro             | a, b type    | condition | a, b format specifier |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_LE   | int          | a <= b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_LT   | int          | a <  b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_GE   | int          | a >= b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_GT   | int          | a >  b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_EQ   | int          | a == b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_INT_NE   | int          | a != b    | %d                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_LE  | unsigned int | a <= b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_LT  | unsigned int | a <  b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_GE  | unsigned int | a >= b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_GT  | unsigned int | a >  b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_EQ  | unsigned int | a == b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_UINT_NE  | unsigned int | a != b    | %u                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_LE   | void*        | a <= b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_LT   | void*        | a <  b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_GE   | void*        | a >= b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_GT   | void*        | a >  b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_EQ   | void*        | a == b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_PTR_NE   | void*        | a != b    | %p                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_LE | float        | a <= b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_LT | float        | a <  b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_GE | float        | a >= b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_GT | float        | a >  b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_EQ | float        | a == b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
-  | PW_CHECK_FLOAT_NE | float        | a != b    | %f                    |
-  +-------------------+--------------+-----------+-----------------------+
+  +-------------------------+--------------+-----------+-----------------------+
+  | Macro                   | a, b type    | condition | a, b format specifier |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_LE         | int          | a <= b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_LT         | int          | a <  b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_GE         | int          | a >= b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_GT         | int          | a >  b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_EQ         | int          | a == b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_INT_NE         | int          | a != b    | %d                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_LE        | unsigned int | a <= b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_LT        | unsigned int | a <  b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_GE        | unsigned int | a >= b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_GT        | unsigned int | a >  b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_EQ        | unsigned int | a == b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_UINT_NE        | unsigned int | a != b    | %u                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_LE         | void*        | a <= b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_LT         | void*        | a <  b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_GE         | void*        | a >= b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_GT         | void*        | a >  b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_EQ         | void*        | a == b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_PTR_NE         | void*        | a != b    | %p                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_LE | float        | a <= b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_LT | float        | a <  b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_GE | float        | a >= b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_GT | float        | a >  b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_EQ | float        | a == b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
+  | PW_CHECK_FLOAT_EXACT_NE | float        | a != b    | %f                    |
+  +-------------------------+--------------+-----------+-----------------------+
 
   The above ``CHECK_*_*()`` are also available in DCHECK variants, which will
   only evaluate their arguments and trigger if the ``NDEBUG`` macro is defined.
 
-  +--------------------+--------------+-----------+-----------------------+
-  | Macro              | a, b type    | condition | a, b format specifier |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_LE   | int          | a <= b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_LT   | int          | a <  b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_GE   | int          | a >= b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_GT   | int          | a >  b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_EQ   | int          | a == b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_INT_NE   | int          | a != b    | %d                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_LE  | unsigned int | a <= b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_LT  | unsigned int | a <  b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_GE  | unsigned int | a >= b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_GT  | unsigned int | a >  b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_EQ  | unsigned int | a == b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_UINT_NE  | unsigned int | a != b    | %u                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_LE   | void*        | a <= b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_LT   | void*        | a <  b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_GE   | void*        | a >= b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_GT   | void*        | a >  b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_EQ   | void*        | a == b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_PTR_NE   | void*        | a != b    | %p                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_LE | float        | a <= b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_LT | float        | a <  b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_GE | float        | a >= b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_GT | float        | a >  b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_EQ | float        | a == b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
-  | PW_DCHECK_FLOAT_NE | float        | a != b    | %f                    |
-  +--------------------+--------------+-----------+-----------------------+
+  +--------------------------+--------------+-----------+----------------------+
+  | Macro                    | a, b type    | condition | a, b format          |
+  |                          |              |           | specifier            |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_LE         | int          | a <= b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_LT         | int          | a <  b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_GE         | int          | a >= b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_GT         | int          | a >  b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_EQ         | int          | a == b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_INT_NE         | int          | a != b    | %d                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_LE        | unsigned int | a <= b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_LT        | unsigned int | a <  b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_GE        | unsigned int | a >= b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_GT        | unsigned int | a >  b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_EQ        | unsigned int | a == b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_UINT_NE        | unsigned int | a != b    | %u                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_LE         | void*        | a <= b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_LT         | void*        | a <  b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_GE         | void*        | a >= b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_GT         | void*        | a >  b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_EQ         | void*        | a == b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_PTR_NE         | void*        | a != b    | %p                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_LE | float        | a <= b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_LT | float        | a <  b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_GE | float        | a >= b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_GT | float        | a >  b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_EQ | float        | a == b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+  | PW_DCHECK_FLOAT_EXACT_NE | float        | a != b    | %f                   |
+  +--------------------------+--------------+-----------+----------------------+
+
+.. attention::
+
+  For float, proper comparator checks which take floating point
+  precision and ergo error accumulation into account are not provided on
+  purpose as this comes with some complexity and requires application
+  specific tolerances in terms of Units of Least Precision (ULP). Instead,
+  we recommend developers carefully consider how floating point precision and
+  error impact the data they are bounding and whether checks are appropriate.
+
+.. cpp:function:: PW_CHECK_FLOAT_NEAR(a, b, abs_tolerance)
+.. cpp:function:: PW_CHECK_FLOAT_NEAR(a, b, abs_tolerance, format, ...)
+.. cpp:function:: PW_DCHECK_FLOAT_NEAR(a, b, abs_tolerance)
+.. cpp:function:: PW_DCHECK_FLOAT_NEAR(a, b, abs_tolerance, format, ...)
+
+  Asserts that ``(a >= b - abs_tolerance) && (a <= b + abs_tolerance)`` is true,
+  where ``a``, ``b``, and ``abs_tolerance`` are converted to ``float``.
+
+  .. note::
+    This also asserts that ``abs_tolerance >= 0``.
+
+  The ``DCHECK`` variants only run if ``NDEBUG`` is defined; otherwise, the
+  entire statement is removed (and the expression not evaluated).
+
+  Example, with no message:
+
+  .. code-block:: cpp
+
+    PW_CHECK_FLOAT_NEAR(cos(0.0f), 1, 0.001);
+
+  Example, with an included message and arguments:
+
+  .. code-block:: cpp
+
+    PW_CHECK_FLOAT_NEAR(FirstOperation(), RedundantOperation(), 0.1,
+                        "System state=%s", SysState());
 
 .. cpp:function:: PW_CHECK_OK(status)
 .. cpp:function:: PW_CHECK_OK(status, format, ...)
diff --git a/pw_assert/public/pw_assert/assert.h b/pw_assert/public/pw_assert/assert.h
index 43cd8ce..36082c5 100644
--- a/pw_assert/public/pw_assert/assert.h
+++ b/pw_assert/public/pw_assert/assert.h
@@ -26,38 +26,65 @@
 //   Trigger a crash with a message. Replaces LOG_FATAL() in other systems.
 //   PW_CRASH(msg, ...)
 //
+//   In all below cases, the message argument is optional:
+//   PW_CHECK_INT_LE(x, y) or
+//   PW_CHECK_INT_LE(x, y, "Was booting %s subsystem", subsystem_name)
+//
 //   Asserts the condition, crashes on failure. Equivalent to assert.
 //   PW_CHECK(condition) or
 //   PW_CHECK(condition, msg, ...)
 //
+//   Some common typed checks.
+//   PW_CHECK_OK(status, msg, ...)  Asserts status == PW_STATUS_OK
+//   PW_CHECK_NOTNULL(ptr, msg, ...)  Asserts ptr != NULL
+//
 //   In many cases an assert is a binary comparison. In those cases, using the
 //   special binary assert macros below for <, <=, >, >=, == enables reporting
 //   the values of the operands in addition to the string of the condition.
 //
-//   In all cases, the message argument is optional:
-//   PW_CHECK_INT_LE(x, y) or
-//   PW_CHECK_INT_LE(x, y, "Was booting %s subsystem", subsystem_name)
-//
 //   Binary comparison asserts for 'int' type ("%d" in format strings):
-//   PW_CHECK_INT_LE  (a, b, msg, ...)  Asserts a <= b
-//   PW_CHECK_INT_LT  (a, b, msg, ...)  Asserts a <  b
-//   PW_CHECK_INT_GE  (a, b, msg, ...)  Asserts a >= b
-//   PW_CHECK_INT_GT  (a, b, msg, ...)  Asserts a >  b
-//   PW_CHECK_INT_EQ  (a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_INT_LE(a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_INT_LT(a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_INT_GE(a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_INT_GT(a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_INT_EQ(a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_INT_NE(a, b, msg, ...)  Asserts a != b
 //
 //   Binary comparison asserts for 'unsigned int' type ("%u" in format strings):
-//   PW_CHECK_UINT_LE (a, b, msg, ...)  Asserts a <= b
-//   PW_CHECK_UINT_LT (a, b, msg, ...)  Asserts a <  b
-//   PW_CHECK_UINT_GE (a, b, msg, ...)  Asserts a >= b
-//   PW_CHECK_UINT_GT (a, b, msg, ...)  Asserts a >  b
-//   PW_CHECK_UINT_EQ (a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_UINT_LE(a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_UINT_LT(a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_UINT_GE(a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_UINT_GT(a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_UINT_EQ(a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_UINT_NE(a, b, msg, ...)  Asserts a != b
+//
+//   Binary comparison asserts for 'void*' type ("%p" in format strings):
+//   PW_CHECK_PTR_LE(a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_PTR_LT(a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_PTR_GE(a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_PTR_GT(a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_PTR_EQ(a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_PTR_NE(a, b, msg, ...)  Asserts a != b
 //
 //   Binary comparison asserts for 'float' type ("%f" in format strings):
-//   PW_CHECK_FLOAT_LE(a, b, msg, ...)  Asserts a <= b
-//   PW_CHECK_FLOAT_LT(a, b, msg, ...)  Asserts a <  b
-//   PW_CHECK_FLOAT_GE(a, b, msg, ...)  Asserts a >= b
-//   PW_CHECK_FLOAT_GT(a, b, msg, ...)  Asserts a >  b
-//   PW_CHECK_FLOAT_EQ(a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_FLOAT_NEAR(a, b, abs_tolerance, msg, ...)
+//     Asserts (a >= (b - abs_tolerance)) && (a <= (b + abs_tolerance))
+//   PW_CHECK_FLOAT_EXACT_LE(a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_FLOAT_EXACT_LT(a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_FLOAT_EXACT_GE(a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_FLOAT_EXACT_GT(a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_FLOAT_EXACT_EQ(a, b, msg, ...)  Asserts a == b
+//   PW_CHECK_FLOAT_EXACT_NE(a, b, msg, ...)  Asserts a != b
+//
+//   The above CHECK_*_*() are also available in DCHECK variants, which will
+//   only evaluate their arguments and trigger if the NDEBUG macro is defined.
+//
+//   Note: For float, proper comparator checks which take floating point
+//   precision and ergo error accumulation into account are not provided on
+//   purpose as this comes with some complexity and requires application
+//   specific tolerances in terms of Units of Least Precision (ULP). Instead,
+//   we recommend developers carefully consider how floating point precision and
+//   error impact the data they are bounding and whether CHECKs are appropriate.
 //
 //   Note: PW_CRASH is the equivalent of LOG_FATAL in other systems, where a
 //   device crash is triggered with a message. In Pigweed, logging and
diff --git a/pw_assert/public/pw_assert/internal/assert_impl.h b/pw_assert/public/pw_assert/internal/assert_impl.h
index 7814340..4b1d594 100644
--- a/pw_assert/public/pw_assert/internal/assert_impl.h
+++ b/pw_assert/public/pw_assert/internal/assert_impl.h
@@ -86,45 +86,47 @@
 #define PW_DCHECK_UINT_EQ(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_UINT_EQ(__VA_ARGS__)
 #define PW_DCHECK_UINT_NE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_UINT_NE(__VA_ARGS__)
 
-// Checks for pointer: LE, LT, GE, GT, EQ, NE.
+// Checks for pointer: LE, LT, GE, GT, EQ, NE, and NOTNULL.
 #define PW_CHECK_PTR_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, void*, "%p", __VA_ARGS__)
 #define PW_CHECK_PTR_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, void*, "%p", __VA_ARGS__)
 #define PW_CHECK_PTR_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, void*, "%p", __VA_ARGS__)
 #define PW_CHECK_PTR_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, void*, "%p", __VA_ARGS__)
 #define PW_CHECK_PTR_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, void*, "%p", __VA_ARGS__)
 #define PW_CHECK_PTR_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, void*, "%p", __VA_ARGS__)
-
-// For implementation simplicity, re-use PTR_NE for NOTNULL.
 #define PW_CHECK_NOTNULL(arga, ...) \
   _PW_CHECK_BINARY_CMP_IMPL(arga, !=, NULL, void*, "%p", __VA_ARGS__)
 
-// Debug checks for pointer: LE, LT, GE, GT, EQ, NE.
+// Debug checks for pointer: LE, LT, GE, GT, EQ, NE, and NOTNULL.
 #define PW_DCHECK_PTR_LE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_LE(__VA_ARGS__)
 #define PW_DCHECK_PTR_LT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_LT(__VA_ARGS__)
 #define PW_DCHECK_PTR_GE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_GE(__VA_ARGS__)
 #define PW_DCHECK_PTR_GT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_GT(__VA_ARGS__)
 #define PW_DCHECK_PTR_EQ(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_EQ(__VA_ARGS__)
 #define PW_DCHECK_PTR_NE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_PTR_NE(__VA_ARGS__)
-
-// For implementation simplicity, re-use PTR_NE for NOTNULL.
 #define PW_DCHECK_NOTNULL(...) \
   if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_NOTNULL(__VA_ARGS__)
 
-// Checks for float: LE, LT, GE, GT, EQ.
-#define PW_CHECK_FLOAT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__)
-#define PW_CHECK_FLOAT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, float, "%f", __VA_ARGS__)
-#define PW_CHECK_FLOAT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__)
-#define PW_CHECK_FLOAT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, float, "%f", __VA_ARGS__)
-#define PW_CHECK_FLOAT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__)
-#define PW_CHECK_FLOAT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, float, "%f", __VA_ARGS__)
+// Checks for float: EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT, EXACT_EQ, EXACT_NE,
+// NEAR.
+#define PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, ...) \
+  _PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EXACT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, float, "%f", __VA_ARGS__)
 
-// Debug checks for float: LE, LT, GE, GT, EQ.
-#define PW_DCHECK_FLOAT_LE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_LE(__VA_ARGS__)
-#define PW_DCHECK_FLOAT_LT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_LT(__VA_ARGS__)
-#define PW_DCHECK_FLOAT_GE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_GE(__VA_ARGS__)
-#define PW_DCHECK_FLOAT_GT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_GT(__VA_ARGS__)
-#define PW_DCHECK_FLOAT_EQ(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EQ(__VA_ARGS__)
-#define PW_DCHECK_FLOAT_NE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_NE(__VA_ARGS__)
+// Debug checks for float: NEAR, EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT,
+// EXACT_EQ.
+#define PW_DCHECK_FLOAT_NEAR(...) \
+  if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_NEAR(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_LE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_LE(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_LT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_LT(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_GE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_GE(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_GT(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_GT(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_EQ(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_EQ(__VA_ARGS__)
+#define PW_DCHECK_FLOAT_EXACT_NE(...) if (PW_ASSERT_ENABLE_DCHECK) PW_CHECK_FLOAT_EXACT_NE(__VA_ARGS__)
 
 // clang-format on
 
@@ -146,7 +148,9 @@
 // =========================================================================
 // Implementation for PW_CHECK
 
-// TODO: Explain why we must expand another time.
+// Two layers of select macros are used to enable the preprocessor to expand
+// macros in the arguments to ultimately token paste the final macro name based
+// on whether there are printf-style arguments.
 #define _PW_CHECK_SELECT_MACRO(condition, has_args, ...) \
   _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, __VA_ARGS__)
 
@@ -165,7 +169,9 @@
 // =========================================================================
 // Implementation for PW_CHECK_<type>_<comparison>
 
-// TODO: Explain why we must expand another time.
+// Two layers of select macros are used to enable the preprocessor to expand
+// macros in the arguments to ultimately token paste the final macro name based
+// on whether there are printf-style arguments.
 #define _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(argument_a_str,       \
                                                  argument_a_val,       \
                                                  comparison_op_str,    \
@@ -232,8 +238,8 @@
                                           type_fmt,              \
                                           __VA_ARGS__)
 
-// For the binary assertions, this private macro is re-used for all the
-// variants. Due to limitations of C formatting, it is necessary to have
+// For the binary assertions, this private macro is re-used for almost all of
+// the variants. Due to limitations of C formatting, it is necessary to have
 // separate macros for the types.
 //
 // The macro avoids evaluating the arguments multiple times at the cost of some
@@ -255,10 +261,42 @@
     }                                                                    \
   } while (0)
 
+// Custom implementation for FLOAT_NEAR which is implemented through two
+// underlying checks which are not trivially replaced through the use of
+// FLOAT_EXACT_LE & FLOAT_EXACT_GE.
+#define _PW_CHECK_FLOAT_NEAR(argument_a, argument_b, abs_tolerance, ...)       \
+  do {                                                                         \
+    PW_CHECK_FLOAT_EXACT_GE(abs_tolerance, 0.0f);                              \
+    float evaluated_argument_a = (float)(argument_a);                          \
+    float evaluated_argument_b_min = (float)(argument_b)-abs_tolerance;        \
+    float evaluated_argument_b_max = (float)(argument_b) + abs_tolerance;      \
+    if (!(evaluated_argument_a >= evaluated_argument_b_min)) {                 \
+      _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a,                    \
+                                               evaluated_argument_a,           \
+                                               ">=",                           \
+                                               #argument_b " - abs_tolerance", \
+                                               evaluated_argument_b_min,       \
+                                               "%f",                           \
+                                               PW_HAS_ARGS(__VA_ARGS__),       \
+                                               __VA_ARGS__);                   \
+    } else if (!(evaluated_argument_a <= evaluated_argument_b_max)) {          \
+      _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a,                    \
+                                               evaluated_argument_a,           \
+                                               "<=",                           \
+                                               #argument_b " + abs_tolerance", \
+                                               evaluated_argument_b_max,       \
+                                               "%f",                           \
+                                               PW_HAS_ARGS(__VA_ARGS__),       \
+                                               __VA_ARGS__);                   \
+    }                                                                          \
+  } while (0)
+
 // =========================================================================
 // Implementation for PW_CHECK_OK
 
-// TODO: Explain why we must expand another time.
+// Two layers of select macros are used to enable the preprocessor to expand
+// macros in the arguments to ultimately token paste the final macro name based
+// on whether there are printf-style arguments.
 #define _PW_CHECK_OK_SELECT_MACRO(                    \
     status_expr_str, status_value_str, has_args, ...) \
   _PW_CHECK_OK_SELECT_MACRO_EXPANDED(                 \
@@ -298,63 +336,65 @@
 #if PW_ASSERT_USE_SHORT_NAMES
 
 // Checks that always run even in production.
-#define CRASH           PW_CRASH
-#define CHECK           PW_CHECK
-#define CHECK_PTR_LE    PW_CHECK_PTR_LE
-#define CHECK_PTR_LT    PW_CHECK_PTR_LT
-#define CHECK_PTR_GE    PW_CHECK_PTR_GE
-#define CHECK_PTR_GT    PW_CHECK_PTR_GT
-#define CHECK_PTR_EQ    PW_CHECK_PTR_EQ
-#define CHECK_PTR_NE    PW_CHECK_PTR_NE
-#define CHECK_NOTNULL   PW_CHECK_NOTNULL
-#define CHECK_INT_LE    PW_CHECK_INT_LE
-#define CHECK_INT_LT    PW_CHECK_INT_LT
-#define CHECK_INT_GE    PW_CHECK_INT_GE
-#define CHECK_INT_GT    PW_CHECK_INT_GT
-#define CHECK_INT_EQ    PW_CHECK_INT_EQ
-#define CHECK_INT_NE    PW_CHECK_INT_NE
-#define CHECK_UINT_LE   PW_CHECK_UINT_LE
-#define CHECK_UINT_LT   PW_CHECK_UINT_LT
-#define CHECK_UINT_GE   PW_CHECK_UINT_GE
-#define CHECK_UINT_GT   PW_CHECK_UINT_GT
-#define CHECK_UINT_EQ   PW_CHECK_UINT_EQ
-#define CHECK_UINT_NE   PW_CHECK_UINT_NE
-#define CHECK_FLOAT_LE  PW_CHECK_FLOAT_LE
-#define CHECK_FLOAT_LT  PW_CHECK_FLOAT_LT
-#define CHECK_FLOAT_GE  PW_CHECK_FLOAT_GE
-#define CHECK_FLOAT_GT  PW_CHECK_FLOAT_GT
-#define CHECK_FLOAT_EQ  PW_CHECK_FLOAT_EQ
-#define CHECK_FLOAT_NE  PW_CHECK_FLOAT_NE
-#define CHECK_OK        PW_CHECK_OK
+#define CRASH                 PW_CRASH
+#define CHECK                 PW_CHECK
+#define CHECK_PTR_LE          PW_CHECK_PTR_LE
+#define CHECK_PTR_LT          PW_CHECK_PTR_LT
+#define CHECK_PTR_GE          PW_CHECK_PTR_GE
+#define CHECK_PTR_GT          PW_CHECK_PTR_GT
+#define CHECK_PTR_EQ          PW_CHECK_PTR_EQ
+#define CHECK_PTR_NE          PW_CHECK_PTR_NE
+#define CHECK_NOTNULL         PW_CHECK_NOTNULL
+#define CHECK_INT_LE          PW_CHECK_INT_LE
+#define CHECK_INT_LT          PW_CHECK_INT_LT
+#define CHECK_INT_GE          PW_CHECK_INT_GE
+#define CHECK_INT_GT          PW_CHECK_INT_GT
+#define CHECK_INT_EQ          PW_CHECK_INT_EQ
+#define CHECK_INT_NE          PW_CHECK_INT_NE
+#define CHECK_UINT_LE         PW_CHECK_UINT_LE
+#define CHECK_UINT_LT         PW_CHECK_UINT_LT
+#define CHECK_UINT_GE         PW_CHECK_UINT_GE
+#define CHECK_UINT_GT         PW_CHECK_UINT_GT
+#define CHECK_UINT_EQ         PW_CHECK_UINT_EQ
+#define CHECK_UINT_NE         PW_CHECK_UINT_NE
+#define CHECK_FLOAT_NEAR      PW_CHECK_FLOAT_NEAR
+#define CHECK_FLOAT_EXACT_LE  PW_CHECK_FLOAT_EXACT_LE
+#define CHECK_FLOAT_EXACT_LT  PW_CHECK_FLOAT_EXACT_LT
+#define CHECK_FLOAT_EXACT_GE  PW_CHECK_FLOAT_EXACT_GE
+#define CHECK_FLOAT_EXACT_GT  PW_CHECK_FLOAT_EXACT_GT
+#define CHECK_FLOAT_EXACT_EQ  PW_CHECK_FLOAT_EXACT_EQ
+#define CHECK_FLOAT_EXACT_NE  PW_CHECK_FLOAT_EXACT_NE
+#define CHECK_OK              PW_CHECK_OK
 
 // Checks that are disabled if NDEBUG is not defined.
-#define DCHECK          PW_DCHECK
-#define DCHECK_PTR_LE   PW_DCHECK_PTR_LE
-#define DCHECK_PTR_LT   PW_DCHECK_PTR_LT
-#define DCHECK_PTR_GE   PW_DCHECK_PTR_GE
-#define DCHECK_PTR_GT   PW_DCHECK_PTR_GT
-#define DCHECK_PTR_EQ   PW_DCHECK_PTR_EQ
-#define DCHECK_PTR_NE   PW_DCHECK_PTR_NE
-#define DCHECK_NOTNULL  PW_DCHECK_NOTNULL
-#define DCHECK_INT_LE   PW_DCHECK_INT_LE
-#define DCHECK_INT_LT   PW_DCHECK_INT_LT
-#define DCHECK_INT_GE   PW_DCHECK_INT_GE
-#define DCHECK_INT_GT   PW_DCHECK_INT_GT
-#define DCHECK_INT_EQ   PW_DCHECK_INT_EQ
-#define DCHECK_INT_NE   PW_DCHECK_INT_NE
-#define DCHECK_UINT_LE  PW_DCHECK_UINT_LE
-#define DCHECK_UINT_LT  PW_DCHECK_UINT_LT
-#define DCHECK_UINT_GE  PW_DCHECK_UINT_GE
-#define DCHECK_UINT_GT  PW_DCHECK_UINT_GT
-#define DCHECK_UINT_EQ  PW_DCHECK_UINT_EQ
-#define DCHECK_UINT_NE  PW_DCHECK_UINT_NE
-#define DCHECK_FLOAT_LE PW_DCHECK_FLOAT_LE
-#define DCHECK_FLOAT_LT PW_DCHECK_FLOAT_LT
-#define DCHECK_FLOAT_GE PW_DCHECK_FLOAT_GE
-#define DCHECK_FLOAT_GT PW_DCHECK_FLOAT_GT
-#define DCHECK_FLOAT_EQ PW_DCHECK_FLOAT_EQ
-#define DCHECK_FLOAT_NE PW_DCHECK_FLOAT_NE
-#define DCHECK_OK       PW_DCHECK_OK
+#define DCHECK                PW_DCHECK
+#define DCHECK_PTR_LE         PW_DCHECK_PTR_LE
+#define DCHECK_PTR_LT         PW_DCHECK_PTR_LT
+#define DCHECK_PTR_GE         PW_DCHECK_PTR_GE
+#define DCHECK_PTR_GT         PW_DCHECK_PTR_GT
+#define DCHECK_PTR_EQ         PW_DCHECK_PTR_EQ
+#define DCHECK_PTR_NE         PW_DCHECK_PTR_NE
+#define DCHECK_NOTNULL        PW_DCHECK_NOTNULL
+#define DCHECK_INT_LE         PW_DCHECK_INT_LE
+#define DCHECK_INT_LT         PW_DCHECK_INT_LT
+#define DCHECK_INT_GE         PW_DCHECK_INT_GE
+#define DCHECK_INT_GT         PW_DCHECK_INT_GT
+#define DCHECK_INT_EQ         PW_DCHECK_INT_EQ
+#define DCHECK_INT_NE         PW_DCHECK_INT_NE
+#define DCHECK_UINT_LE        PW_DCHECK_UINT_LE
+#define DCHECK_UINT_LT        PW_DCHECK_UINT_LT
+#define DCHECK_UINT_GE        PW_DCHECK_UINT_GE
+#define DCHECK_UINT_GT        PW_DCHECK_UINT_GT
+#define DCHECK_UINT_EQ        PW_DCHECK_UINT_EQ
+#define DCHECK_UINT_NE        PW_DCHECK_UINT_NE
+#define DCHECK_FLOAT_NEAR     PW_DCHECK_FLOAT_NEAR
+#define DCHECK_FLOAT_EXACT_LT PW_DCHECK_FLOAT_EXACT_LT
+#define DCHECK_FLOAT_EXACT_LE PW_DCHECK_FLOAT_EXACT_LE
+#define DCHECK_FLOAT_EXACT_GT PW_DCHECK_FLOAT_EXACT_GT
+#define DCHECK_FLOAT_EXACT_GE PW_DCHECK_FLOAT_EXACT_GE
+#define DCHECK_FLOAT_EXACT_EQ PW_DCHECK_FLOAT_EXACT_EQ
+#define DCHECK_FLOAT_EXACT_NE PW_DCHECK_FLOAT_EXACT_NE
+#define DCHECK_OK             PW_DCHECK_OK
 
 #endif  // PW_ASSERT_SHORT_NAMES
 // clang-format on
diff --git a/pw_assert_basic/public/pw_assert_basic/assert_basic.h b/pw_assert_basic/public/pw_assert_basic/assert_basic.h
index f2186c2..269c0a6 100644
--- a/pw_assert_basic/public/pw_assert_basic/assert_basic.h
+++ b/pw_assert_basic/public/pw_assert_basic/assert_basic.h
@@ -52,7 +52,7 @@
 
 // Sample assert failure message produced by the below implementation:
 //
-//   Check failed: current_sensor (=610) < new_sensor (=50): More details!
+//   Check failed: current_sensor (=610) < new_sensor (=50). More details!
 //
 // Putting the value next to the operand makes the string easier to read.