Skip to content

BUG: cast NaT to NaN when casting to float/complex - #32777

Open
Hee-San wants to merge 2 commits into
numpy:mainfrom
Hee-San:nat-to-nan-float-cast
Open

Hee-San wants to merge 2 commits into
numpy:mainfrom
Hee-San:nat-to-nan-float-cast

Conversation

@Hee-San

@Hee-San Hee-San commented Sep 24, 2026 •

Copy link
Copy Markdown

PR summary

Closes #26177

This PR changes the casting of np.datetime64('NaT') and np.timedelta64('NaT') to floating-point and complex types so that NaT is converted to NaN.

Internally, NaT is stored as the minimum int64 value, so casting to a float type used that value as-is, producing -9.2e18. For float16, the value is out of range, so it became -inf along with an overflow warning.
With this change, NaT is now converted to NaN without any warning.

Before the fix (numpy 2.5.3):

>>> np.array([np.datetime64('NaT', 's'), 1], dtype=object).astype(float)
array([-9.22337204e+18,  1.00000000e+00])
>>> np.array([np.timedelta64('NaT', 's'), 1+2j], dtype=object).astype(complex)
array([-9.22337204e+18+0.j,  1.00000000e+00+2.j])
>>> np.array(['NaT', '2024-01-01'], dtype='M8[D]').astype(float)
array([-9.22337204e+18,  1.97230000e+04])
>>> np.array(['NaT', 5], dtype='m8[s]').astype(np.float16)
<stdin>:1: RuntimeWarning: overflow encountered in cast
array([-inf,   5.], dtype=float16)
>>> np.float64(np.datetime64('NaT', 's'))
np.float64(-9.223372036854776e+18)

After the fix (local build of main with this patch applied):

>>> np.array([np.datetime64('NaT', 's'), 1], dtype=object).astype(float)
array([nan,  1.])
>>> np.array([np.timedelta64('NaT', 's'), 1+2j], dtype=object).astype(complex)
array([nan+0.j,  1.+2.j])
>>> np.array(['NaT', '2024-01-01'], dtype='M8[D]').astype(float)
array([   nan, 19723.])
>>> np.array(['NaT', 5], dtype='m8[s]').astype(np.float16)
array([nan,  5.], dtype=float16)
>>> np.float64(np.datetime64('NaT', 's'))
np.float64(nan)

Casting to integer and bool types is unchanged.
Integer types have no equivalent of NaN, and with int64 the NaT sentinel is preserved as the minimum value, so the existing behavior already round-trips back to NaT when cast back to a datetime type.
For bool, NaT evaluates to True, just like NaN does.

Same output before and after the fix:

>>> np.array(['NaT', 5], dtype='m8[s]').astype(np.int64)
array([-9223372036854775808,                    5])
>>> np.array(['NaT', 5], dtype='m8[s]').astype(bool)
array([ True,  True])

First time contributor introduction

Back in my student days (up until about five years ago), I used NumPy for studying and doing research in computer science and the sciences.
I now work as a software engineer, and I don't get to use NumPy in my day job.
Still, I'd like to give back to the various open-source projects I've benefited from over the years by contributing to them.
I picked a long-standing issue that had no PR yet and that I felt I could handle even though I don't use NumPy much these days.

AI Disclosure

Tool used: Anthropic's Claude Code.
Investigation: I used it to identify which conversion functions the cast goes through, to confirm the reproduction, and to understand the surrounding related functions.
Code: The C changes and the tests were generated by the AI. I then had it explain the code to me line by line so I understood it, and I directed it to revise the tests in terms of how they are written and what they cover — specifically how the functions are split up, matching the style of the existing tests, and where comments are placed. I judged the AI-written code for the fix itself to be fine as-is, so I adopted it unchanged. The AI also ran the build and the tests.
Writing: The issue comments, the commit messages, and this PR description were written by me in Japanese and simple English, then translated and polished by the AI.

I apologize for PR #32775, where I opened a draft PR that didn't follow the template.
That PR was created by me manually, not submitted automatically by an AI agent.

NaT is stored as the minimum int64 value, and casts to floating-point
types used that value as is, giving -9.2e18. For float16 the value is
out of range, so the cast gave -inf along with an overflow warning.

The reverse cast already maps NaN to NaT. To match it, map NaT to NaN.
For complex types, the real part is NaN and the imaginary part is 0.

Casts to integer types and bool are unchanged. Integer types have no
equivalent of NaN, and for int64 NaT stays the minimum value, so
casting back to datetime gives NaT again. For bool, NaT is true, the
same as NaN.

Closes numpy#26177
@Hee-San
Hee-San marked this pull request as ready for review September 24, 2026 12:47

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: NaT is incorrectly cast inside object arrays

1 participant