diff --git a/applications/test/bitSet2/Test-bitSet2.C b/applications/test/bitSet2/Test-bitSet2.C
index 9c62bd7e7a2536acd9c4533821644d5184388b97..b76048b01d508cc0c90c85b3898383b894ed15b2 100644
--- a/applications/test/bitSet2/Test-bitSet2.C
+++ b/applications/test/bitSet2/Test-bitSet2.C
@@ -40,12 +40,19 @@ Description
 
 using namespace Foam;
 
-inline Ostream& report
-(
-    const bitSet& bitset,
-    bool showBits = false,
-    bool debugOutput = false
-)
+
+inline Ostream& extent(const bitSet& bitset)
+{
+    Info<< "first: " << bitset.find_first()
+        << " last: " << bitset.find_last()
+        << " first_not: " << bitset.find_first_not()
+        << endl;
+
+    return Info;
+}
+
+
+inline Ostream& info(const bitSet& bitset)
 {
     Info<< "size=" << bitset.size() << "/" << bitset.capacity()
         << " count=" << bitset.count()
@@ -54,17 +61,11 @@ inline Ostream& report
         << " any:" << bitset.any()
         << " none:" << bitset.none() << nl;
 
-    Info<< "values: " << flatOutput(bitset) << nl;
-    if (showBits)
-    {
-        bitset.printBits(Info, debugOutput) << nl;
-    }
-
     return Info;
 }
 
 
-inline Ostream& report(const UList<bool>& bools)
+inline Ostream& info(const UList<bool>& bools)
 {
     Info<< "size=" << bools.size()
         << " count=" << BitOps::count(bools)
@@ -77,6 +78,31 @@ inline Ostream& report(const UList<bool>& bools)
 }
 
 
+inline Ostream& report
+(
+    const bitSet& bitset,
+    bool showBits = false,
+    bool debugOutput = false
+)
+{
+    info(bitset);
+
+    Info<< "values: " << flatOutput(bitset) << nl;
+    if (showBits)
+    {
+        bitset.printBits(Info, debugOutput) << nl;
+    }
+
+    return Info;
+}
+
+
+inline Ostream& report(const UList<bool>& bools)
+{
+    return info(bools);
+}
+
+
 template<class UIntType>
 std::string toString(UIntType value, char off='.', char on='1')
 {
@@ -153,10 +179,7 @@ int main(int argc, char *argv[])
     compare(list1, "...................1..1..1..1..1");
 
     report(list1, true);
-
-
-    Info<< "first: " << list1.find_first()
-        << " last: " << list1.find_last() << endl;
+    extent(list1);
 
     Info<< "iterate through:";
     for (const label idx : list1)
@@ -172,6 +195,21 @@ int main(int argc, char *argv[])
 
     Info<< "\nflipped bit pattern\n";
     report(list2, true);
+    extent(list2);
+
+    Info<< "\nsparse set\n";
+    {
+        bitSet sparse(1000);
+        sparse.set(300);
+
+        info(sparse);
+        extent(sparse);
+
+        sparse.set(0);
+
+        info(sparse);
+        extent(sparse);
+    }
 
     // set every other on
     forAll(list2, i)
diff --git a/src/OpenFOAM/containers/Bits/bitSet/bitSet.H b/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
index 32b104df2c4d7da178495968115d49e231ad77bb..8f2cfae00312ddf3ad86711b4eb18e20e06f8622 100644
--- a/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
+++ b/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
@@ -65,6 +65,15 @@ class bitSet
 :
     public PackedList<1>
 {
+private:
+
+    // Private Member Functions
+
+        //- Find the first block with a '0' bit
+        //  \return block number or -1 if the set is empty or all bits are on.
+        inline label first_not_block() const;
+
+
 protected:
 
     // Protected Member Functions
@@ -203,12 +212,18 @@ public:
         //  \note Method name compatibility with std::bitset
         inline bool test(const label pos) const;
 
-        //- Locate the first bit set.
+        //- Locate the first bit that is set.
         //  \return the location or -1 if there are no bits set.
         //
         //  \note Method name compatibility with boost::dynamic_bitset
         inline label find_first() const;
 
+        //- Locate the first bit that is unset.
+        //  \return the location or -1 if the set is empty or all bits are on.
+        //
+        //  \note Provided for symmetry with find_first()
+        inline label find_first_not() const;
+
         //- Locate the last bit set.
         //  \return the location or -1 if there are no bits set.
         //
diff --git a/src/OpenFOAM/containers/Bits/bitSet/bitSetI.H b/src/OpenFOAM/containers/Bits/bitSet/bitSetI.H
index 0ab1bb59abb7367c4be7394195775e5142ef5843..413aa87c3357418c04438e7042509f38555de0c9 100644
--- a/src/OpenFOAM/containers/Bits/bitSet/bitSetI.H
+++ b/src/OpenFOAM/containers/Bits/bitSet/bitSetI.H
@@ -23,6 +23,53 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+inline Foam::label Foam::bitSet::first_not_block() const
+{
+    if (empty())
+    {
+        return -1;
+    }
+
+    // Use complement to change 0 <-> 1 and check if any 1's now appear
+
+    const label nblocks = num_blocks(size());
+
+    // Extra bits in the final block?
+    const unsigned int off = size() % elem_per_block;
+
+    if (!off)
+    {
+        for (label blocki=0; blocki < nblocks; ++blocki)
+        {
+            if (~(blocks_[blocki]))
+            {
+                return blocki;
+            }
+        }
+    }
+    else
+    {
+        for (label blocki=0; blocki < nblocks-1; ++blocki)
+        {
+            if (~(blocks_[blocki]))
+            {
+                return blocki;
+            }
+        }
+
+        // The final block needs masking
+        if (~(blocks_[nblocks-1]) & mask_lower(off))
+        {
+            return nblocks-1;
+        }
+    }
+
+    return -1;
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline constexpr Foam::bitSet::bitSet() noexcept
@@ -257,6 +304,38 @@ inline Foam::label Foam::bitSet::find_first() const
 }
 
 
+inline Foam::label Foam::bitSet::find_first_not() const
+{
+    const label blocki = first_not_block();
+
+    if (blocki >= 0)
+    {
+        label pos = (blocki * elem_per_block);
+
+        // Detect first '0' bit by checking the complement.
+
+        // No special masking for the final block, that was already checked
+        // in the first_not_block() call.
+
+        for
+        (
+            unsigned int blockval = ~(blocks_[blocki]);
+            blockval;
+            blockval >>= 1u
+        )
+        {
+            if (blockval & 1u)
+            {
+                return pos;
+            }
+            ++pos;
+        }
+    }
+
+    return -1;
+}
+
+
 inline Foam::label Foam::bitSet::find_last() const
 {
     // Process block-wise, detecting any '1' bits
@@ -339,44 +418,7 @@ inline Foam::label Foam::bitSet::find_next(label pos) const
 
 inline bool Foam::bitSet::all() const
 {
-    if (empty())
-    {
-        return true;
-    }
-
-    // Use complement to change 0 <-> 1 and check if any 1's now appear
-
-    const label nblocks = num_blocks(size());
-
-    // Extra bits in the final block?
-    const unsigned int off = size() % elem_per_block;
-
-    if (!off)
-    {
-        for (label blocki=0; blocki < nblocks; ++blocki)
-        {
-            if (~(blocks_[blocki]))
-            {
-                return false;
-            }
-        }
-    }
-    else
-    {
-        for (label blocki=0; blocki < nblocks-1; ++blocki)
-        {
-            if (~(blocks_[blocki]))
-            {
-                return false;
-            }
-        }
-
-        // Verify the final block, with masking
-
-        return (!(~blocks_[nblocks-1] & mask_lower(off)));
-    }
-
-    return true;
+    return -1 == first_not_block();
 }
 
 
@@ -388,7 +430,10 @@ inline bool Foam::bitSet::any() const
 
         for (label blocki=0; blocki < nblocks; ++blocki)
         {
-            if (blocks_[blocki]) return true;
+            if (blocks_[blocki])
+            {
+                return true;
+            }
         }
     }
 
@@ -421,7 +466,7 @@ inline unsigned int Foam::bitSet::count(const bool on) const
 
     if (!on)
     {
-        // Return the number of bits that are off.
+        // Return the number of bits that are OFF.
         return (unsigned(size()) - total);
     }