Repository: pandas-dev/pandas

Issue:
BUG: pd.SparseDtype('int', 0) == pd.SparseDtype('int', pd.NA) returns pd.NA
### Pandas version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the [latest version](https://pandas.pydata.org/docs/whatsnew/index.html) of pandas.

- [ ] I have confirmed this bug exists on the [main branch](https://pandas.pydata.org/docs/dev/getting_started/install.html#installing-the-development-version-of-pandas) of pandas.


### Reproducible Example

```python
import pandas as pd

int_fill_0 = pd.SparseDtype('int', 0)
int_fill_n = pd.SparseDtype('int', pd.NA)

r = (int_fill_0 == int_fill_n)
# pd.NA

r_reverse = (int_fill_n == int_fill_0)
# False
```


### Issue Description

Equality comparison of SparseDtype filled by pd.NA and SparseDtype filled by non-na returns pd.NA, and is not symmetric.


### Expected Behavior

It should return False.

### Installed Versions

<details>

Replace this line with the output of pd.show_versions()

</details>



Discussion:
take


Patch:
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index ca2ca07ff2fae..16be9e0a4fc34 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -280,6 +280,7 @@ Performance improvements
 
 Bug fixes
 ~~~~~~~~~
+- Fixed bug in :class:`SparseDtype` for equal comparison with na fill value. (:issue:`54770`)
 - Fixed bug in :meth:`DataFrame.join` inconsistently setting result index name (:issue:`55815`)
 - Fixed bug in :meth:`DataFrame.to_string` that raised ``StopIteration`` with nested DataFrames. (:issue:`16098`)
 - Fixed bug in :meth:`DataFrame.update` bool dtype being converted to object (:issue:`55509`)
diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py
index 2bb2556c88204..f94d32a3b8547 100644
--- a/pandas/core/dtypes/dtypes.py
+++ b/pandas/core/dtypes/dtypes.py
@@ -1705,17 +1705,15 @@ def __eq__(self, other: object) -> bool:
 
         if isinstance(other, type(self)):
             subtype = self.subtype == other.subtype
-            if self._is_na_fill_value:
+            if self._is_na_fill_value or other._is_na_fill_value:
                 # this case is complicated by two things:
                 # SparseDtype(float, float(nan)) == SparseDtype(float, np.nan)
                 # SparseDtype(float, np.nan)     != SparseDtype(float, pd.NaT)
                 # i.e. we want to treat any floating-point NaN as equal, but
                 # not a floating-point NaN and a datetime NaT.
-                fill_value = (
-                    other._is_na_fill_value
-                    and isinstance(self.fill_value, type(other.fill_value))
-                    or isinstance(other.fill_value, type(self.fill_value))
-                )
+                fill_value = isinstance(
+                    self.fill_value, type(other.fill_value)
+                ) or isinstance(other.fill_value, type(self.fill_value))
             else:
                 with warnings.catch_warnings():
                     # Ignore spurious numpy warning


Test patch:
diff --git a/pandas/tests/arrays/sparse/test_dtype.py b/pandas/tests/arrays/sparse/test_dtype.py
index 6fcbfe96a3df7..6f0d41333f2fd 100644
--- a/pandas/tests/arrays/sparse/test_dtype.py
+++ b/pandas/tests/arrays/sparse/test_dtype.py
@@ -68,6 +68,14 @@ def test_nans_equal():
     assert b == a
 
 
+def test_nans_not_equal():
+    # GH 54770
+    a = SparseDtype(float, 0)
+    b = SparseDtype(float, pd.NA)
+    assert a != b
+    assert b != a
+
+
 with warnings.catch_warnings():
     msg = "Allowing arbitrary scalar fill_value in SparseDtype is deprecated"
     warnings.filterwarnings("ignore", msg, category=FutureWarning)
diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py
index c3a1d584170fb..cbca306ab0041 100644
--- a/pandas/tests/extension/test_sparse.py
+++ b/pandas/tests/extension/test_sparse.py
@@ -240,10 +240,6 @@ def test_fillna_limit_backfill(self, data_missing):
             super().test_fillna_limit_backfill(data_missing)
 
     def test_fillna_no_op_returns_copy(self, data, request):
-        if np.isnan(data.fill_value):
-            request.applymarker(
-                pytest.mark.xfail(reason="returns array with different fill value")
-            )
         super().test_fillna_no_op_returns_copy(data)
 
     @pytest.mark.xfail(reason="Unsupported")
@@ -400,6 +396,8 @@ def test_arith_frame_with_scalar(self, data, all_arithmetic_operators, request):
             "rmul",
             "floordiv",
             "rfloordiv",
+            "truediv",
+            "rtruediv",
             "pow",
             "mod",
             "rmod",