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.