Skip to content

MAINT: np.spacing: avoid warnings with NaN input - #32602

Merged
ngoldbaum merged 8 commits into
numpy:mainfrom
mdhaber:fix-spacing-nan-warning
Sep 15, 2026
Merged

ngoldbaum merged 8 commits into
numpy:mainfrom
mdhaber:fix-spacing-nan-warning

Conversation

@mdhaber

@mdhaber mdhaber commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

PR summary

On some platforms, including my Windows machine:

import numpy as np
np.spacing(np.nan)

emits RuntimeWarning: invalid value encountered in spacing. Based on the behavior of similar functions and the comments in the code, I don't think this was intentional. This PR eliminates the inadvertent warning by special-casing NaN input.

Additional information

I encountered this while developing SciPy. I thought it would be better to fix the source rather than leaving the np.errstate in the code indefinitely.

There are comments in the tests about similar issues in ceil and arccos. I set Codex on those, too, but the diffs are a bit larger, so I thought I'd start with this simple one. If you'd be be interested in a PR to fix those, I can update this PR to include those commits, available here:
https://github.com/mdhaber/numpy/pull/new/fix_spacing_ceil_arccos_nan_warning

I'd also love it if we could eliminate the warnings I get with np.log(np.nan + 0j). np.logaddexp(x, y) where either x or y is NaN, and similar issues with np.exp I've only seen in SciPy CI.

AI Disclosure

OpenAI Codex was used to inspect the relevant implementation and tests, draft
the code changes and regression tests, and prepare this AI disclosure.

@ikrommyd ikrommyd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I only have a minor styling comment.

In addition, my AI agent says these nits. I don't know if we wanna do anything about them now

  1. NaN sign is dropped for float16 only. The half path returns the canonical NaN, while the float, double, and longdouble paths return the input, so a negative NaN keeps its sign there. npy_half_nextafter also canonicalizes, so it is consistent within half, but returning h would make all dtypes match.
  2. spacing(inf) raises invalid for float16 but not for float32 or float64, so the dtypes disagree on infinity the same way they used to on NaN.

Comment thread numpy/_core/src/npymath/halffloat.cpp Outdated
Comment on lines 70 to 75
if (h_exp == 0x7c00u && h_sig != 0) {
ret = NPY_HALF_NAN;
} else if (h_exp == 0x7c00u) {
#if NPY_HALF_GENERATE_INVALID
npy_set_floatstatus_invalid();
#endif

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this reads a little better? (if I formatted it correctly here)

Suggested change
if (h_exp == 0x7c00u && h_sig != 0) {
ret = NPY_HALF_NAN;
} else if (h_exp == 0x7c00u) {
#if NPY_HALF_GENERATE_INVALID
npy_set_floatstatus_invalid();
#endif
if (h_exp == 0x7c00u) {
/* NaN passes through quietly; only inf is an invalid operand. */
if (h_sig == 0) {
#if NPY_HALF_GENERATE_INVALID
npy_set_floatstatus_invalid();
#endif
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

@ngoldbaum

Copy link
Copy Markdown
Member

I think this is an improvement but it needs a release note describing what it does

You're changing behavior that was intentionally chosen back in 2010 in 89d8512 for float16. We reverted the nextafter part of that commit in #15988 but never touched spacing, and I think this generally improves API consistency.

@mdhaber

mdhaber commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ngoldbaum, added the note. Looks like some other change.rst were just formatted with a bullet rather than the full heading, so I went that way, but feel free to suggest/commit changes inline.

@ngoldbaum

Copy link
Copy Markdown
Member

Thanks @mdhaber!

@ngoldbaum
ngoldbaum merged commit 8ba190e into numpy:main Sep 15, 2026
92 checks passed
@mdhaber

mdhaber commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@ikrommyd @ngoldbaum Would you consider a similar PR that avoids the warning for log(NaN + x*1j) (finite x)? It sounds like discussion in the past has agreed that the warning is optional / platform-dependent, but NaN input -> NaN result of elementary functions typically does not deserve a warning (e.g. np.sin(np.nan + 1j)). It would be helpful to avoid it consistently across platforms so the user does not accidentally suppress meaningful warnings alongside it. Similarly, I'd like to fix logsumexp(x, y) with x, y, or both NaN.

Details

My first concern is performance: I'd rather not incur the overhead of np.errstate multiple times in iterative code, especially in the typical case where there are no NaNs.

In [7]:  %timeit with np.errstate(invalid='ignore'): np.log(1j)
1.15 μs ± 10.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

In [8]:  %timeit np.log(1j)
171 ns ± 0.767 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

I could silence them throughout, but that risks ignoring warnings that should be emitted.
The other concern is that use of np.errstate complicates review, I think. "What warnings are being suppressed here? Is that really OK?" It may be OK to warn when new NaNs are produced, but I'd rather be able to rely on a consistent no-warning policy for elementary functions when no new NaNs are created.

I know this has been discussed to death. I'm just trying to guage whether the conclusion these days is that suppression is OK, and it's just a matter of doing the work.

@ngoldbaum

ngoldbaum commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

I know this has been discussed to death.

All before my time. I think if you cite #12236 and #15988 as prior times when we loosened NaN warnings for exactly this case you could probably get a PR over the line. Of course that only helps NumPy 2.6 and newer...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants