You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
is_null(..., nan_is_null=true) did not detect NaN values stored in a dictionary's values, so NaN entries in dictionary-encoded float/double/half-float arrays were reported as valid instead of null. Null dictionary values must also keep being reported as null with both default options and nan_is_null=true.
What changes are included in this PR?
Adds NaN and null detection to is_null for dictionary-encoded floating-point arrays. For dictionaries of floating types, the output bitmap is built by calling is_null on the dictionary values (which also catches NaN when nan_is_null=true), mapping that through the indices with take, and OR-ing it into the existing null-index bitmap. take runs without bounds checking, matching how other compute kernels treat dictionary indices as already valid. Every other is_null path is unchanged.
Are these changes tested?
Yes. A single parameterized test in scalar_validity_test.cc covers float16, float32, and float64 dictionaries: NaN values, null dictionary entries, null indices, default options, and empty arrays.
Are there any user-facing changes?
Yes. is_null(..., nan_is_null=true) now reports NaN entries in dictionary-encoded floating-point arrays as null. Null dictionary values were already reported as null and remain so.
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes is_null(..., nan_is_null=true) for dictionary-encoded floating-point arrays by detecting NaN values in the dictionary and marking corresponding slots as null in the output.
Changes:
Added dictionary-aware NaN detection for is_null when nan_is_null is enabled.
Implemented templated helpers to dispatch over dictionary index integer types and float value types (float/double/half-float).
Added includes needed for dictionary type inspection and safe casting.
The reason will be displayed to describe this comment to others. Learn more.
Could you use the Arrow PR title template:- GH-<Issue Number>: [<Component>] <Title>
If there isn't an existed issue, you can create a new one.
And could you use Arrow PR template for description
Rationale for this change
What changes are included in this PR?
Are these changes tested?
Are there any user-facing changes?
shoemoney
changed the title
fix(arrow): handle nan_is_null for dictionary-encoded floats
MINOR: [C++] Fix is_null nan_is_null for dictionary-encoded float arrays
Aug 27, 2026
Updated the title and description to match the template, and fixed both issues from the review: null index slots are now skipped before dereferencing the dictionary, and the default case in the index-type dispatch fails fast instead of assuming int32.
@shoemoney Hi, thanks for submitting this. Some high-level comments after taking a quick look:
This is not a MINOR change, can you open a separate issue for the bug?
Dictionary values (not only indices) can be null too, does your PR handle that?
A potentially simpler solution is to call is_null on the dictionary values:
a. dict_is_null = is_null(dict_array.dictionary)
b. temp_is_null = take(dict_is_null, dict_array.indices)
c. final_is_null = BitmapOrNot(temp_is_null.values, temp_is_null.validity_bitmap)
Thanks for the look. On (1), opened #51127 and retitled against it. On (2), no: the current patch only skips null index slots, so a null dictionary value referenced by a valid index still gets dereferenced, which is the gap you're pointing at. Your is_null(dictionary) → take → BitmapOrNot approach handles both cases in one pass and is simpler than what I have, so I'd rather switch to it than extend the current loop — I'll do that in this PR unless you or @zanmato1984 would prefer it start fresh.
Switched to your approach in (3). The NaN loop and its per-index-type dispatch are gone, replaced by is_null on the dictionary values, take through the indices, and an OR into the output bitmap. The output already holds the inverted index validity bitmap at that point, so the take result's values bitmap goes in as is, no masking needed. take runs with bounds checking, so an out-of-range index now errors instead of reading past the dictionary.
That also answers (2): a null dictionary value behind a valid index is reported as null now. New test IsNullDictionaryNullValues fails at index 1 on the previous commit and passes on this one. I added IsNullDictionaryNanIsNullUnsignedIndices too, since the explicit index-type dispatch that used to be covered is gone.
The path still only runs for nan_is_null=true with a floating-point value type, so is_null with default options is unchanged for every dictionary type. Tell me if you would rather null dictionary values be reported for all dictionary types, that is a one-line widening of the condition.
Rebased onto main, commit 1d2b266. Local run of arrow-compute-scalar-utility-test: 99 tests from 41 suites passed.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The new code uses checked_cast unqualified in two places, which should be ::arrow::internal::checked_cast (or imported), and will otherwise fail to compile.
Get a fresh assessment by requesting another Copilot review.
Unqualified checked_cast here should be ::arrow::internal::checked_cast (or add a using internal::checked_cast; in scope); as written this is a compile error.
…float arrays
is_null(..., nan_is_null=true) did not detect NaN values in
dictionary-encoded floating-point arrays. IsNullExec only checked the
top-level type id for floating point, so dictionary-encoded arrays
(type id DICTIONARY) skipped the NaN path entirely and NaN values in
the dictionary were never marked null.
Adds SetNanBitsFromDictionary, which calls is_null on the dictionary
values with nan_is_null=true, maps the result through the indices with
take, and ORs the resulting bitmap into the output. Hooks it into
SetLogicalNullBits's DICTIONARY branch, alongside the existing
dict_util::SetLogicalNullBits call that already reports null indices
and null dictionary entries.
Rebased onto the SetLogicalNullBits refactor that landed on main after
this branch was opened. dict_util::SetLogicalNullBits now reports a
null dictionary entry as a logical null unconditionally, not only
under nan_is_null, so the IsNullDictionaryNullValues test's
default_options expectation is updated to match (index 1 is now null
even without nan_is_null).
Fixed the malformed-index crash in 61bef91. For floating dictionaries with nan_is_null=true, checked take now runs before the dictionary validity lookup. Null-index payloads remain ignored, and other validity paths are unchanged.
The expanded bounds regression terminated with SIGSEGV (exit 139) before this correction and passes afterward. Locally, arrow-compute-scalar-utility-test --gtest_brief=1 passes all 102 tests from 41 suites on macOS arm64 Debug. clang-format 18.1.8 and cpplint 1.6.1 also pass. Full Arrow CI was not run locally.
The reason will be displayed to describe this comment to others. Learn more.
Replaced the separate half-float test with one loop over float16(), float32(), and float64(). In ca96314. The consolidated regression passes for all three types, and the rebuilt scalar utility suite passes all 97 tests on macOS arm64 Debug. clang-format and cpplint also pass.
The ARM64 macOS C++ job failed because arrow-s3fs-test hit the 300-second timeout on all three CTest attempts. The other 109 tests passed, including arrow-compute-scalar-utility-test. The fork run of ca9631491 passed all 110 tests on ARM64 macOS, including S3. This PR changes no filesystem or CI code. The timeout cause is still unconfirmed, so I have left the code unchanged.
The reason will be displayed to describe this comment to others. Learn more.
Changed in a78401dca: BitmapOrNot combines the taken bits with inverted index validity in one pass; arrays without null indices use CopyBitmap. The float16/32/64 test now also covers non-null indices pointing to null/NaN values. Rebuilt scalar-utility suite: 97 tests passed (macOS arm64 Debug); clang-format 18.1.8 and cpplint 1.6.1 passed. AI-assisted implementation and validation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rationale for this change
is_null(..., nan_is_null=true)did not detect NaN values stored in a dictionary's values, so NaN entries in dictionary-encoded float/double/half-float arrays were reported as valid instead of null. Null dictionary values must also keep being reported as null with both default options andnan_is_null=true.What changes are included in this PR?
Adds NaN and null detection to
is_nullfor dictionary-encoded floating-point arrays. For dictionaries of floating types, the output bitmap is built by callingis_nullon the dictionary values (which also catches NaN whennan_is_null=true), mapping that through the indices withtake, and OR-ing it into the existing null-index bitmap.takeruns without bounds checking, matching how other compute kernels treat dictionary indices as already valid. Every otheris_nullpath is unchanged.Are these changes tested?
Yes. A single parameterized test in
scalar_validity_test.cccovers float16, float32, and float64 dictionaries: NaN values, null dictionary entries, null indices, default options, and empty arrays.Are there any user-facing changes?
Yes.
is_null(..., nan_is_null=true)now reports NaN entries in dictionary-encoded floating-point arrays as null. Null dictionary values were already reported as null and remain so.