@@ -28,7 +28,7 @@ use arrow::datatypes::DataType::{LargeList, List, Null};
2828use arrow:: datatypes:: { DataType , Field , FieldRef } ;
2929use arrow:: row:: { RowConverter , SortField } ;
3030use datafusion_common:: cast:: { as_large_list_array, as_list_array} ;
31- use datafusion_common:: utils:: ListCoercion ;
31+ use datafusion_common:: utils:: { ListCoercion , normalize_float_zero } ;
3232use datafusion_common:: {
3333 Result , assert_eq_or_internal_err, exec_err, internal_err, utils:: take_function_args,
3434} ;
@@ -351,21 +351,27 @@ fn generic_set_lists<OffsetSize: OffsetSizeTrait>(
351351
352352 let converter = RowConverter :: new ( vec ! [ SortField :: new( l. value_type( ) ) ] ) ?;
353353
354+ // Normalize -0.0 → +0.0 so RowConverter (which uses IEEE 754 totalOrder
355+ // and treats ±0 as distinct) groups them together. Use the normalized
356+ // arrays for both row conversion and the final output values.
357+ let l_values_norm = normalize_float_zero ( l. values ( ) ) ;
358+ let r_values_norm = normalize_float_zero ( r. values ( ) ) ;
359+
354360 // Only convert the visible portion of the values array. For sliced
355361 // ListArrays, values() returns the full underlying array but only
356362 // elements between the first and last offset are referenced.
357363 let l_first = l. offsets ( ) [ 0 ] . as_usize ( ) ;
358364 let l_len = l. offsets ( ) [ l. len ( ) ] . as_usize ( ) - l_first;
359- let rows_l = converter. convert_columns ( & [ l . values ( ) . slice ( l_first, l_len) ] ) ?;
365+ let rows_l = converter. convert_columns ( & [ l_values_norm . slice ( l_first, l_len) ] ) ?;
360366
361367 let r_first = r. offsets ( ) [ 0 ] . as_usize ( ) ;
362368 let r_len = r. offsets ( ) [ r. len ( ) ] . as_usize ( ) - r_first;
363- let rows_r = converter. convert_columns ( & [ r . values ( ) . slice ( r_first, r_len) ] ) ?;
369+ let rows_r = converter. convert_columns ( & [ r_values_norm . slice ( r_first, r_len) ] ) ?;
364370
365371 // Combine the *sliced* value arrays so 0-based indices from the row
366372 // converter map directly into the concatenated array.
367- let l_values = l . values ( ) . slice ( l_first, l_len) ;
368- let r_values = r . values ( ) . slice ( r_first, r_len) ;
373+ let l_values = l_values_norm . slice ( l_first, l_len) ;
374+ let r_values = r_values_norm . slice ( r_first, r_len) ;
369375 let combined_values = concat ( & [ l_values. as_ref ( ) , r_values. as_ref ( ) ] ) ?;
370376 let r_offset = l_len;
371377
@@ -558,13 +564,18 @@ fn general_array_distinct<OffsetSize: OffsetSizeTrait>(
558564
559565 let converter = RowConverter :: new ( vec ! [ SortField :: new( dt. clone( ) ) ] ) ?;
560566
567+ // Normalize -0.0 → +0.0 so RowConverter (which uses IEEE 754 totalOrder
568+ // and treats ±0 as distinct) groups them together, and so the output
569+ // carries the canonical sign.
570+ let values_norm = normalize_float_zero ( array. values ( ) ) ;
571+
561572 // Only convert the visible portion of the values array. For sliced
562573 // ListArrays, values() returns the full underlying array but only
563574 // elements between the first and last offset are referenced.
564575 let first_offset = value_offsets[ 0 ] . as_usize ( ) ;
565576 let visible_len = value_offsets[ array. len ( ) ] . as_usize ( ) - first_offset;
566577 let rows =
567- converter. convert_columns ( & [ array . values ( ) . slice ( first_offset, visible_len) ] ) ?;
578+ converter. convert_columns ( & [ values_norm . slice ( first_offset, visible_len) ] ) ?;
568579
569580 let mut indices: Vec < usize > = Vec :: with_capacity ( rows. num_rows ( ) ) ;
570581 let mut seen = HashSet :: new ( ) ;
@@ -593,19 +604,19 @@ fn general_array_distinct<OffsetSize: OffsetSizeTrait>(
593604 }
594605
595606 // Gather distinct values in a single pass, using the computed `indices`.
596- // Indices are absolute positions in array.values() (first_offset was added
597- // back when collecting them), so we can take directly from the full values.
607+ // Indices are absolute positions in the (normalized) values array, so we
608+ // can take directly from the full values.
598609 // Use UInt64Array for LargeList to support values arrays exceeding u32::MAX.
599610 let final_values = if indices. is_empty ( ) {
600611 new_empty_array ( & dt)
601612 } else if OffsetSize :: IS_LARGE {
602613 let indices =
603614 UInt64Array :: from ( indices. into_iter ( ) . map ( |i| i as u64 ) . collect :: < Vec < _ > > ( ) ) ;
604- take ( array . values ( ) . as_ref ( ) , & indices, None ) ?
615+ take ( values_norm . as_ref ( ) , & indices, None ) ?
605616 } else {
606617 let indices =
607618 UInt32Array :: from ( indices. into_iter ( ) . map ( |i| i as u32 ) . collect :: < Vec < _ > > ( ) ) ;
608- take ( array . values ( ) . as_ref ( ) , & indices, None ) ?
619+ take ( values_norm . as_ref ( ) , & indices, None ) ?
609620 } ;
610621
611622 Ok ( Arc :: new ( GenericListArray :: < OffsetSize > :: try_new (
0 commit comments