diff --git a/changes/3144.bugfix.rst b/changes/3144.bugfix.rst new file mode 100644 index 0000000000..8dde317bb8 --- /dev/null +++ b/changes/3144.bugfix.rst @@ -0,0 +1 @@ +Ensure that -0.0 is not considered equal to 0.0 when checking if all the values in a chunk are equal to an array's fill value.``` diff --git a/src/zarr/core/buffer/core.py b/src/zarr/core/buffer/core.py index 19125b838f..89e34f82d0 100644 --- a/src/zarr/core/buffer/core.py +++ b/src/zarr/core/buffer/core.py @@ -523,6 +523,12 @@ def all_equal(self, other: Any, equal_nan: bool = True) -> bool: if other is None: # Handle None fill_value for Zarr V2 return False + if other == 0.0 and self._data.dtype.kind not in ("U", "S", "T", "O", "V"): + # Handle positive and negative zero + if np.any(self._data): # Check for any truthy value + return False + # Check signs: + return np.array_equiv(np.signbit(self._data), np.signbit(other)) # use array_equal to obtain equal_nan=True functionality # Since fill-value is a scalar, isn't there a faster path than allocating a new array for fill value # every single time we have to write data? diff --git a/tests/test_array.py b/tests/test_array.py index fe23bc1284..e16acc1b36 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -904,6 +904,30 @@ def test_write_empty_chunks_behavior( assert arr.nchunks_initialized == arr.nchunks +@pytest.mark.parametrize("store", ["memory"], indirect=True) +@pytest.mark.parametrize("fill_value", [0.0, -0.0]) +def test_write_empty_chunks_negative_zero( + zarr_format: ZarrFormat, store: MemoryStore, fill_value: float +) -> None: + # regression test for https://github.com/zarr-developers/zarr-python/issues/3144 + + arr = zarr.create_array( + store=store, + shape=(2,), + zarr_format=zarr_format, + dtype="f4", + fill_value=fill_value, + chunks=(1,), + config={"write_empty_chunks": False}, + ) + + assert arr.nchunks_initialized == 0 + + # initialize the with the negated fill value (-0.0 for +0.0, +0.0 for -0.0) + arr[:] = -fill_value + assert arr.nchunks_initialized == arr.nchunks + + @pytest.mark.parametrize( ("fill_value", "expected"), [