arrow_cast/cast/
numeric.rs1use crate::cast::*;
8
9pub(crate) fn cast_numeric_arrays<FROM, TO>(
11 from: &dyn Array,
12 cast_options: &CastOptions,
13) -> Result<ArrayRef, ArrowError>
14where
15 FROM: ArrowPrimitiveType,
16 TO: ArrowPrimitiveType,
17 FROM::Native: NumCast + NumericNative,
18 TO::Native: NumCast + NumericNative,
19{
20 let from = from.as_primitive::<FROM>();
21 let array: PrimitiveArray<TO> = if const {
22 is_infallible_numeric_cast(
23 <FROM::Native as NumericNative>::KIND,
24 <TO::Native as NumericNative>::KIND,
25 )
26 } {
27 from.unary(|v| num_cast(v).expect("numeric cast is infallible"))
29 } else if cast_options.safe {
30 from.unary_opt(num_cast)
32 } else {
33 from.try_unary(|v| {
35 num_cast(v).ok_or_else(|| {
36 ArrowError::CastError(format!("Can't cast value {v:?} to type {}", TO::DATA_TYPE))
37 })
38 })?
39 };
40 Ok(Arc::new(array))
41}
42
43#[derive(Clone, Copy)]
46pub(crate) struct NumericKind {
47 float: bool,
48 signed: bool,
49 bits: u32,
50}
51
52pub(crate) trait NumericNative {
54 const KIND: NumericKind;
55}
56
57macro_rules! numeric_native {
58 ($($t:ty => $float:literal, $signed:literal;)*) => {$(
59 impl NumericNative for $t {
60 const KIND: NumericKind = NumericKind {
61 float: $float,
62 signed: $signed,
63 bits: 8 * std::mem::size_of::<$t>() as u32,
64 };
65 }
66 )*};
67}
68
69numeric_native! {
70 u8 => false, false;
71 u16 => false, false;
72 u32 => false, false;
73 u64 => false, false;
74 i8 => false, true;
75 i16 => false, true;
76 i32 => false, true;
77 i64 => false, true;
78 half::f16 => true, true;
79 f32 => true, true;
80 f64 => true, true;
81}
82
83pub(crate) const fn is_infallible_numeric_cast(from: NumericKind, to: NumericKind) -> bool {
91 if to.float {
92 true
93 } else if from.float {
94 false
95 } else if from.signed == to.signed {
96 to.bits >= from.bits
97 } else {
98 !from.signed && to.bits > from.bits
100 }
101}
102
103#[inline]
106pub fn num_cast<I, O>(value: I) -> Option<O>
107where
108 I: NumCast,
109 O: NumCast,
110{
111 num_traits::cast::cast::<I, O>(value)
112}
113
114pub(crate) fn cast_numeric_to_bool<FROM>(from: &dyn Array) -> Result<ArrayRef, ArrowError>
118where
119 FROM: ArrowPrimitiveType,
120{
121 Ok(Arc::new(BooleanArray::from_unary(
122 from.as_primitive::<FROM>(),
123 cast_num_to_bool,
124 )))
125}
126
127#[inline]
129pub fn cast_num_to_bool<I>(value: I) -> bool
130where
131 I: Default + PartialEq,
132{
133 value != I::default()
134}
135
136pub(crate) fn cast_bool_to_numeric<TO>(
140 from: &dyn Array,
141 cast_options: &CastOptions,
142) -> Result<ArrayRef, ArrowError>
143where
144 TO: ArrowPrimitiveType,
145{
146 Ok(Arc::new(bool_to_numeric_cast::<TO>(
147 from.as_any().downcast_ref::<BooleanArray>().unwrap(),
148 cast_options,
149 )))
150}
151
152fn bool_to_numeric_cast<T>(from: &BooleanArray, _cast_options: &CastOptions) -> PrimitiveArray<T>
153where
154 T: ArrowPrimitiveType,
155{
156 let to_numeric = |bits: u64, i: usize| {
157 if bits & (1 << i) != 0 {
158 T::Native::ONE
159 } else {
160 T::Native::ZERO
161 }
162 };
163 let chunks = from.values().bit_chunks();
166 let mut values = Vec::with_capacity(from.len());
167 for bits in &chunks {
168 values.extend((0..64).map(|i| to_numeric(bits, i)));
169 }
170 let bits = chunks.remainder_bits();
171 values.extend((0..chunks.remainder_len()).map(|i| to_numeric(bits, i)));
172 PrimitiveArray::new(values.into(), from.nulls().cloned())
173}
174
175#[inline]
177pub fn single_bool_to_numeric<O>(value: bool) -> Option<O>
178where
179 O: num_traits::NumCast + Default,
180{
181 if value {
182 num_traits::cast::cast(1)
184 } else {
185 Some(O::default())
186 }
187}