datafusion_functions/math/
signum.rs1use std::sync::Arc;
19
20use arrow::array::AsArray;
21use arrow::datatypes::DataType::{Float32, Float64};
22use arrow::datatypes::{DataType, Float32Type, Float64Type};
23
24use datafusion_common::utils::take_function_args;
25use datafusion_common::{Result, ScalarValue, internal_err};
26use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
27use datafusion_expr::{
28 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
29 Volatility,
30};
31use datafusion_macros::user_doc;
32
33#[user_doc(
34 doc_section(label = "Math Functions"),
35 description = r#"Returns the sign of a number.
36Negative numbers return `-1`.
37Zero and positive numbers return `1`."#,
38 syntax_example = "signum(numeric_expression)",
39 standard_argument(name = "numeric_expression", prefix = "Numeric"),
40 sql_example = r#"```sql
41> SELECT signum(-42);
42+-------------+
43| signum(-42) |
44+-------------+
45| -1 |
46+-------------+
47```"#
48)]
49#[derive(Debug, PartialEq, Eq, Hash)]
50pub struct SignumFunc {
51 signature: Signature,
52}
53
54impl Default for SignumFunc {
55 fn default() -> Self {
56 SignumFunc::new()
57 }
58}
59
60impl SignumFunc {
61 pub fn new() -> Self {
62 use DataType::*;
63 Self {
64 signature: Signature::uniform(
65 1,
66 vec![Float64, Float32],
67 Volatility::Immutable,
68 ),
69 }
70 }
71}
72
73impl ScalarUDFImpl for SignumFunc {
74 fn name(&self) -> &str {
75 "signum"
76 }
77
78 fn signature(&self) -> &Signature {
79 &self.signature
80 }
81
82 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
83 match &arg_types[0] {
84 Float32 => Ok(Float32),
85 _ => Ok(Float64),
86 }
87 }
88
89 fn is_strict(&self) -> bool {
90 true
91 }
92
93 fn output_ordering(&self, input: &[ExprProperties]) -> Result<SortProperties> {
94 Ok(input[0].sort_properties)
96 }
97
98 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
99 let return_type = args.return_type().clone();
100 let [arg] = take_function_args(self.name(), args.args)?;
101
102 match arg {
103 ColumnarValue::Scalar(scalar) => {
104 if scalar.is_null() {
105 return ColumnarValue::Scalar(ScalarValue::Null)
106 .cast_to(&return_type, None);
107 }
108
109 match scalar {
110 ScalarValue::Float64(Some(v)) => {
111 let result = if v == 0.0 { 0.0 } else { v.signum() };
112 Ok(ColumnarValue::Scalar(ScalarValue::Float64(Some(result))))
113 }
114 ScalarValue::Float32(Some(v)) => {
115 let result = if v == 0.0 { 0.0 } else { v.signum() };
116 Ok(ColumnarValue::Scalar(ScalarValue::Float32(Some(result))))
117 }
118 _ => {
119 internal_err!(
120 "Unexpected scalar type for signum: {:?}",
121 scalar.data_type()
122 )
123 }
124 }
125 }
126 ColumnarValue::Array(array) => match array.data_type() {
127 Float64 => Ok(ColumnarValue::Array(Arc::new(
128 array.as_primitive::<Float64Type>().unary::<_, Float64Type>(
129 |x: f64| {
130 if x == 0.0 { 0.0 } else { x.signum() }
131 },
132 ),
133 ))),
134 Float32 => Ok(ColumnarValue::Array(Arc::new(
135 array.as_primitive::<Float32Type>().unary::<_, Float32Type>(
136 |x: f32| {
137 if x == 0.0 { 0.0 } else { x.signum() }
138 },
139 ),
140 ))),
141 other => {
142 internal_err!("Unsupported data type {other:?} for function signum")
143 }
144 },
145 }
146 }
147
148 fn documentation(&self) -> Option<&Documentation> {
149 self.doc()
150 }
151}
152
153#[cfg(test)]
154mod test {
155 use std::sync::Arc;
156
157 use arrow::array::{ArrayRef, Float32Array, Float64Array};
158 use arrow::datatypes::{DataType, Field};
159 use datafusion_common::cast::{as_float32_array, as_float64_array};
160 use datafusion_common::config::ConfigOptions;
161 use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl};
162
163 use crate::math::signum::SignumFunc;
164
165 #[test]
166 fn test_signum_f32() {
167 let array = Arc::new(Float32Array::from(vec![
168 -1.0,
169 -0.0,
170 0.0,
171 1.0,
172 -0.01,
173 0.01,
174 f32::NAN,
175 f32::INFINITY,
176 f32::NEG_INFINITY,
177 ]));
178 let arg_fields = vec![Field::new("a", DataType::Float32, false).into()];
179 let args = ScalarFunctionArgs {
180 args: vec![ColumnarValue::Array(Arc::clone(&array) as ArrayRef)],
181 arg_fields,
182 number_rows: array.len(),
183 return_field: Field::new("f", DataType::Float32, true).into(),
184 config_options: Arc::new(ConfigOptions::default()),
185 };
186 let result = SignumFunc::new()
187 .invoke_with_args(args)
188 .expect("failed to initialize function signum");
189
190 match result {
191 ColumnarValue::Array(arr) => {
192 let floats = as_float32_array(&arr)
193 .expect("failed to convert result to a Float32Array");
194
195 assert_eq!(floats.len(), 9);
196 assert_eq!(floats.value(0), -1.0);
197 assert_eq!(floats.value(1), 0.0);
198 assert_eq!(floats.value(2), 0.0);
199 assert_eq!(floats.value(3), 1.0);
200 assert_eq!(floats.value(4), -1.0);
201 assert_eq!(floats.value(5), 1.0);
202 assert!(floats.value(6).is_nan());
203 assert_eq!(floats.value(7), 1.0);
204 assert_eq!(floats.value(8), -1.0);
205 }
206 ColumnarValue::Scalar(_) => {
207 panic!("Expected an array value")
208 }
209 }
210 }
211
212 #[test]
213 fn test_signum_f64() {
214 let array = Arc::new(Float64Array::from(vec![
215 -1.0,
216 -0.0,
217 0.0,
218 1.0,
219 -0.01,
220 0.01,
221 f64::NAN,
222 f64::INFINITY,
223 f64::NEG_INFINITY,
224 ]));
225 let arg_fields = vec![Field::new("a", DataType::Float64, false).into()];
226 let args = ScalarFunctionArgs {
227 args: vec![ColumnarValue::Array(Arc::clone(&array) as ArrayRef)],
228 arg_fields,
229 number_rows: array.len(),
230 return_field: Field::new("f", DataType::Float64, true).into(),
231 config_options: Arc::new(ConfigOptions::default()),
232 };
233 let result = SignumFunc::new()
234 .invoke_with_args(args)
235 .expect("failed to initialize function signum");
236
237 match result {
238 ColumnarValue::Array(arr) => {
239 let floats = as_float64_array(&arr)
240 .expect("failed to convert result to a Float32Array");
241
242 assert_eq!(floats.len(), 9);
243 assert_eq!(floats.value(0), -1.0);
244 assert_eq!(floats.value(1), 0.0);
245 assert_eq!(floats.value(2), 0.0);
246 assert_eq!(floats.value(3), 1.0);
247 assert_eq!(floats.value(4), -1.0);
248 assert_eq!(floats.value(5), 1.0);
249 assert!(floats.value(6).is_nan());
250 assert_eq!(floats.value(7), 1.0);
251 assert_eq!(floats.value(8), -1.0);
252 }
253 ColumnarValue::Scalar(_) => {
254 panic!("Expected an array value")
255 }
256 }
257 }
258}